Move CargoCrate to SlottedContainer

implement necessary changes to MatteryMenu, MatterySlot and panels
to reflect networking slot filters as part of container state, and not as part of menu slot
This commit is contained in:
DBotThePony 2025-02-28 21:57:28 +07:00
parent 30263bf30e
commit f79b49d422
Signed by: DBot
GPG Key ID: DCC23B5715498507
14 changed files with 170 additions and 201 deletions

View File

@ -32,6 +32,8 @@ import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
@ -43,20 +45,24 @@ class CargoCrateBlockEntity(
p_155229_: BlockPos, p_155229_: BlockPos,
p_155230_: BlockState p_155230_: BlockState
) : MatteryDeviceBlockEntity(MBlockEntities.CARGO_CRATE, p_155229_, p_155230_) { ) : MatteryDeviceBlockEntity(MBlockEntities.CARGO_CRATE, p_155229_, p_155230_) {
val container = MatteryContainer(this::setChanged, CAPACITY).also(::addDroppableContainer) private inner class Slot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super<FilteredContainerSlot>.canAutomationPlaceItem(itemStack) && lootTable == null
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && lootTable == null
}
}
val container = SlottedContainer.Builder()
.add(CAPACITY, ::Slot)
.onChanged(::setChanged)
.build()
.also(::addDroppableContainer)
private var interactingPlayers = 0 private var interactingPlayers = 0
val handler = container.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return lootTable == null
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return lootTable == null
}
})
override fun beforeDroppingItems(oldBlockState: BlockState, level: Level, blockPos: BlockPos, newBlockState: BlockState, movedByPiston: Boolean) { override fun beforeDroppingItems(oldBlockState: BlockState, level: Level, blockPos: BlockPos, newBlockState: BlockState, movedByPiston: Boolean) {
unpackLootTable() unpackLootTable()
} }
@ -88,7 +94,7 @@ class CargoCrateBlockEntity(
} }
init { init {
exposeGlobally(Capabilities.ItemHandler.BLOCK, handler) exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
savetablesLevel.stateful(::container, INVENTORY_KEY) savetablesLevel.stateful(::container, INVENTORY_KEY)
} }

View File

@ -348,8 +348,6 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
return false return false
} }
override var slotFilter: Item? by slot.filter!!
} }
} }

View File

@ -20,7 +20,7 @@ class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Compon
val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6) val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6)
for (slot in menu.storageSlots) for (slot in menu.storageSlots)
UserFilteredSlotPanel.of(this, grid, slot) UserFilteredSlotPanel(this, grid, slot)
val controls = DeviceControls(this, frame) val controls = DeviceControls(this, frame)

View File

@ -18,10 +18,6 @@ open class InventorySlotPanel<out S : MatteryScreen<*>, out T : MatteryMenu.Inve
x: Float = 0f, x: Float = 0f,
y: Float = 0f, y: Float = 0f,
) : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, SIZE, SIZE) { ) : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, SIZE, SIZE) {
override var slotFilter: Item?
get() = slot.filter?.get()
set(value) { slot.filter?.accept(value) }
override fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { override fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
if (slot.chargeFlag?.get() == true) { if (slot.chargeFlag?.get() == true) {
Widgets18.CHARGE_SLOT_BACKGROUND.render(graphics, 0f, 0f, width, height) Widgets18.CHARGE_SLOT_BACKGROUND.render(graphics, 0f, 0f, width, height)

View File

@ -5,9 +5,13 @@ package ru.dbotthepony.mc.otm.client.screen.panels.slot
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.client.renderer.GameRenderer import net.minecraft.client.renderer.GameRenderer
import net.minecraft.network.chat.Component
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.Slot import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.Widgets18
@ -15,6 +19,10 @@ import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.compat.itemborders.isItemBordersLoaded import ru.dbotthepony.mc.otm.compat.itemborders.isItemBordersLoaded
import ru.dbotthepony.mc.otm.compat.itemborders.renderSlotBorder import ru.dbotthepony.mc.otm.compat.itemborders.renderSlotBorder
import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.container.util.containerSlotOrNull
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import javax.annotation.Nonnull import javax.annotation.Nonnull
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -52,6 +60,31 @@ open class SlotPanel<out S : MatteryScreen<*>, out T : Slot>(
} }
} }
protected open fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {}
override fun renderSlotBackground(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
super.renderSlotBackground(graphics, mouseX, mouseY, partialTick)
val containerSlot = slot.container.containerSlotOrNull(slot.slotIndex)
if (containerSlot is IFilteredContainerSlot) {
renderBackgroundBeforeFilter(graphics, mouseX, mouseY, partialTick)
if (containerSlot.filter !== null) {
if (containerSlot.filter !== Items.AIR) {
val itemStack = ItemStack(containerSlot.filter!!, 1)
screen.renderItemStack(graphics, itemStack, null)
clearDepth(graphics)
graphics.renderRect(0f, 0f, width, height, color = SLOT_FILTER_COLOR)
} else {
graphics.renderRect(0f, 0f, width, height, color = SLOT_BLOCK_COLOR)
}
}
}
}
override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
slot.x = absoluteX.roundToInt() - screen.guiLeft slot.x = absoluteX.roundToInt() - screen.guiLeft
slot.y = absoluteY.roundToInt() - screen.guiTop slot.y = absoluteY.roundToInt() - screen.guiTop
@ -121,9 +154,46 @@ open class SlotPanel<out S : MatteryScreen<*>, out T : Slot>(
} }
override fun innerRenderTooltips(@Nonnull graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean { override fun innerRenderTooltips(@Nonnull graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
val slot = slot.container.containerSlotOrNull(slot.containerSlot) as? IFilteredContainerSlot
if (isHovered && slot?.filter != null && slot.filter !== Items.AIR && itemStack.isEmpty) {
val itemstack = ItemStack(slot.filter!!, 1)
graphics.renderComponentTooltip(
IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: font,
getItemStackTooltip(itemstack).toMutableList().also {
it.add(0, TranslatableComponent("otm.gui.slot_filter.filtered").withStyle(ChatFormatting.GRAY))
it.add(1, TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
it.add(2, TextComponent(""))
},
mouseX.toInt(),
mouseY.toInt(),
itemstack
)
return true
} else if (isHovered && slot?.filter === Items.AIR && itemStack.isEmpty) {
graphics.renderComponentTooltip(
font,
ArrayList<Component>().also {
it.add(TranslatableComponent("otm.gui.slot_filter.forbidden").withStyle(ChatFormatting.GRAY))
it.add(TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
},
mouseX.toInt(),
mouseY.toInt()
)
return true
}
// no op, screen does it for us (completely) // no op, screen does it for us (completely)
return false return false
} }
companion object {
val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150)
val SLOT_BLOCK_COLOR = RGBAColor(219, 113, 113, 150)
}
} }
fun <S : MatteryScreen<*>, T : Slot> BatterySlotPanel( fun <S : MatteryScreen<*>, T : Slot> BatterySlotPanel(

View File

@ -1,28 +1,16 @@
package ru.dbotthepony.mc.otm.client.screen.panels.slot package ru.dbotthepony.mc.otm.client.screen.panels.slot
import com.mojang.blaze3d.platform.InputConstants import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.isCtrlDown import ru.dbotthepony.mc.otm.client.isCtrlDown
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.playGuiClickSound import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.container.util.containerSlot
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.menu.UserFilteredSlot import ru.dbotthepony.mc.otm.menu.UserFilteredSlot
abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>( open class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : UserFilteredSlot>(
screen: S, screen: S,
parent: EditablePanel<*>?, parent: EditablePanel<*>?,
slot: T, slot: T,
@ -31,77 +19,24 @@ abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
width: Float = SIZE, width: Float = SIZE,
height: Float = SIZE, height: Float = SIZE,
) : SlotPanel<S, T>(screen, parent, slot, x, y, width, height) { ) : SlotPanel<S, T>(screen, parent, slot, x, y, width, height) {
abstract var slotFilter: Item?
protected open fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {}
override fun renderSlotBackground(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
super.renderSlotBackground(graphics, mouseX, mouseY, partialTick)
renderBackgroundBeforeFilter(graphics, mouseX, mouseY, partialTick)
if (slotFilter != null) {
if (slotFilter !== Items.AIR) {
val itemStack = ItemStack(slotFilter!!, 1)
screen.renderItemStack(graphics, itemStack, null)
clearDepth(graphics)
graphics.renderRect(0f, 0f, width, height, color = SLOT_FILTER_COLOR)
} else {
graphics.renderRect(0f, 0f, width, height, color = SLOT_BLOCK_COLOR)
}
}
}
override fun innerRenderTooltips(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered && slotFilter != null && slotFilter !== Items.AIR && itemStack.isEmpty) {
val itemstack = ItemStack(slotFilter!!, 1)
graphics.renderComponentTooltip(
IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: font,
getItemStackTooltip(itemstack).toMutableList().also {
it.add(0, TranslatableComponent("otm.gui.slot_filter.filtered").withStyle(ChatFormatting.GRAY))
it.add(1, TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
it.add(2, TextComponent(""))
},
mouseX.toInt(),
mouseY.toInt(),
itemstack
)
return true
} else if (isHovered && slotFilter === Items.AIR && itemStack.isEmpty) {
graphics.renderComponentTooltip(
font,
ArrayList<Component>().also {
it.add(TranslatableComponent("otm.gui.slot_filter.forbidden").withStyle(ChatFormatting.GRAY))
it.add(TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
},
mouseX.toInt(),
mouseY.toInt()
)
return true
}
return super.innerRenderTooltips(graphics, mouseX, mouseY, partialTick)
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
if (slot.filterInput == null)
return super.mouseClickedInner(x, y, button)
val containerSlot = slot.containerSlot() as IFilteredContainerSlot
if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) { if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) {
if (slotFilter === null) { if (containerSlot.filter === null) {
if (screen.menu.carried.isEmpty) { if (screen.menu.carried.isEmpty) {
slotFilter = slot.item.item slot.filterInput!!.accept(slot.item.item)
} else { } else {
slotFilter = screen.menu.carried.item slot.filterInput!!.accept(screen.menu.carried.item)
} }
} else { } else {
slotFilter = null slot.filterInput!!.accept(null)
} }
playGuiClickSound() playGuiClickSound()
return true return true
} else { } else {
return super.mouseClickedInner(x, y, button) return super.mouseClickedInner(x, y, button)
@ -115,40 +50,4 @@ abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
return super.mouseReleasedInner(x, y, button) return super.mouseReleasedInner(x, y, button)
} }
companion object {
val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150)
val SLOT_BLOCK_COLOR = RGBAColor(219, 113, 113, 150)
fun <S : MatteryScreen<*>, T : Slot> of(
screen: S,
parent: EditablePanel<*>?,
slot: T,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
filter: Delegate<Item?>
): UserFilteredSlotPanel<S, T> {
return object : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height) {
override var slotFilter: Item? by filter
}
}
fun <S : MatteryScreen<*>, T : UserFilteredSlot> of(
screen: S,
parent: EditablePanel<*>?,
slot: T,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
): UserFilteredSlotPanel<S, T> {
return object : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height) {
override var slotFilter: Item?
get() = slot.filter?.get()
set(value) { slot.filter?.accept(value) }
}
}
}
} }

View File

@ -20,7 +20,7 @@ class ItemHatchScreen(menu: ItemHatchMenu, inventory: Inventory, title: Componen
val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6) val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6)
for (slot in menu.storageSlots) for (slot in menu.storageSlots)
UserFilteredSlotPanel.of(this, grid, slot) UserFilteredSlotPanel(this, grid, slot)
if (menu.isInput) { if (menu.isInput) {
val controls = DeviceControls(this, frame) val controls = DeviceControls(this, frame)

View File

@ -4,7 +4,7 @@ import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler import net.neoforged.neoforge.items.IItemHandler
/** /**
* Slot of [ISlottedContainer], with additional methods to implement interaction behavior for both for players and mechanisms * Slot of [IAutomatedContainer], with additional methods to implement interaction behavior for both for players and mechanisms
*/ */
interface IAutomatedContainerSlot : IContainerSlot { interface IAutomatedContainerSlot : IContainerSlot {
fun canAutomationPlaceItem(itemStack: ItemStack): Boolean { fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {

View File

@ -1,6 +1,5 @@
package ru.dbotthepony.mc.otm.container package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
@ -15,7 +14,6 @@ interface IFilteredContainerSlot : IContainerSlot {
val hasFilter: Boolean val hasFilter: Boolean
get() = filter != null get() = filter != null
fun testSlotFilter(itemStack: ItemStack): Boolean { fun testSlotFilter(itemStack: ItemStack): Boolean {
return testSlotFilter(itemStack.item) return testSlotFilter(itemStack.item)
} }

View File

@ -83,7 +83,7 @@ open class ContainerSlot(
return ItemStack.EMPTY return ItemStack.EMPTY
} }
if (item.count >= count) { if (item.count <= count) {
this.item = ItemStack.EMPTY this.item = ItemStack.EMPTY
return item return item
} else { } else {

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.container.util package ru.dbotthepony.mc.otm.container.util
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.container.IContainerSlot import ru.dbotthepony.mc.otm.container.IContainerSlot
@ -19,6 +20,25 @@ fun Container.containerSlot(slot: Int): IContainerSlot {
} }
} }
/**
* Returns [IContainerSlot] only if this container is [IEnhancedContainer]
*/
fun Container.containerSlotOrNull(slot: Int): IContainerSlot? {
if (this is IEnhancedContainer) {
return containerSlot(slot)
} else {
return null
}
}
fun Slot.containerSlot(): IContainerSlot {
return container.containerSlot(slotIndex)
}
fun Slot.containerSlotOrNull(): IContainerSlot? {
return container.containerSlotOrNull(slotIndex)
}
operator fun Container.iterator(): Iterator<ItemStack> { operator fun Container.iterator(): Iterator<ItemStack> {
if (this is IEnhancedContainer) { if (this is IEnhancedContainer) {
return iterator() return iterator()

View File

@ -38,9 +38,11 @@ import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots import ru.dbotthepony.mc.otm.compat.cos.cosmeticArmorSlots
import ru.dbotthepony.mc.otm.compat.curios.curiosSlots import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.container.IMatteryContainer import ru.dbotthepony.mc.otm.container.IMatteryContainer
import ru.dbotthepony.mc.otm.container.computeSortedIndices import ru.dbotthepony.mc.otm.container.computeSortedIndices
import ru.dbotthepony.mc.otm.container.sortWithIndices import ru.dbotthepony.mc.otm.container.sortWithIndices
import ru.dbotthepony.mc.otm.container.util.containerSlotOrNull
import ru.dbotthepony.mc.otm.core.ResourceLocation import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.core.collect.ConditionalSet import ru.dbotthepony.mc.otm.core.collect.ConditionalSet
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
@ -207,7 +209,7 @@ abstract class MatteryMenu(
var sortInventoryInput: SortInput? = null var sortInventoryInput: SortInput? = null
private set private set
val playerSortSettings = IItemStackSortingSettings.inputs(this, player.matteryPlayer?.sortingSettings) val playerSortSettings = IItemStackSortingSettings.inputs(this, player.matteryPlayer.sortingSettings)
var offhandSlot: InventorySlot? = null var offhandSlot: InventorySlot? = null
protected set protected set
@ -221,7 +223,7 @@ abstract class MatteryMenu(
protected var inventorySlotIndexStart = 0 protected var inventorySlotIndexStart = 0
protected var inventorySlotIndexEnd = 0 protected var inventorySlotIndexEnd = 0
open inner class InventorySlot(container: Container, index: Int, addFilter: Boolean = false) : UserFilteredSlot(container, index, 0, 0) { open inner class InventorySlot(container: Container, index: Int) : UserFilteredSlot(container, index, 0, 0) {
override fun mayPlace(itemStack: ItemStack): Boolean { override fun mayPlace(itemStack: ItemStack): Boolean {
return !isInventorySlotLocked(index) && super.mayPlace(itemStack) return !isInventorySlotLocked(index) && super.mayPlace(itemStack)
} }
@ -236,15 +238,6 @@ abstract class MatteryMenu(
init { init {
val mattery = player.matteryPlayer val mattery = player.matteryPlayer
if (addFilter) {
val mContainer = container as IMatteryContainer
filter = Delegate.Of(
getter = { mContainer.getSlotFilter(slotIndex) },
setter = nullableItemInput(true) { mContainer.setSlotFilter(slotIndex, it) }::accept
)
}
if (mattery.hasExopack) { if (mattery.hasExopack) {
chargeFlag = Delegate.Of( chargeFlag = Delegate.Of(
getter = { slotIndex in mattery.slotsChargeFlag }, getter = { slotIndex in mattery.slotsChargeFlag },
@ -302,7 +295,7 @@ abstract class MatteryMenu(
for (i in 0 until if (mattery.hasExopack) mattery.combinedInventory.containerSize else mattery.wrappedItemInventory.containerSize) { for (i in 0 until if (mattery.hasExopack) mattery.combinedInventory.containerSize else mattery.wrappedItemInventory.containerSize) {
if (i in Inventory.INVENTORY_SIZE until player.inventory.containerSize) continue if (i in Inventory.INVENTORY_SIZE until player.inventory.containerSize) continue
val slot = InventorySlot(mattery.combinedInventory, i, true) val slot = InventorySlot(mattery.combinedInventory, i)
_playerInventorySlots.add(slot) _playerInventorySlots.add(slot)
@ -392,21 +385,8 @@ abstract class MatteryMenu(
if (!seenSlots.add(pSlot)) if (!seenSlots.add(pSlot))
return pSlot return pSlot
if (pSlot is UserFilteredSlot && !pSlot.hasSetFilter) { if (pSlot is MatterySlot) {
val container = pSlot.container pSlot.setupNetworkControls(this)
val input: PlayerInput<Item?>
val field: Delegate<Item?>
if (container is IMatteryContainer) {
input = PlayerInput(StreamCodecs.ITEM_TYPE_NULLABLE, handler = { container.setSlotFilter(pSlot.slotIndex, it) })
field = mSynchronizer.add(delegate = { container.getSlotFilter(pSlot.slotIndex) }, StreamCodecs.ITEM_TYPE_NULLABLE)
} else {
input = PlayerInput(StreamCodecs.ITEM_TYPE_NULLABLE, handler = { throw UnsupportedOperationException() })
field = mSynchronizer.add(delegate = { null }, StreamCodecs.ITEM_TYPE_NULLABLE)
}
pSlot.filter = Delegate.Of(getter = field::get, setter = input::accept)
} }
return super.addSlot(pSlot) return super.addSlot(pSlot)
@ -487,7 +467,7 @@ abstract class MatteryMenu(
val copy = slot.item.copy() val copy = slot.item.copy()
var any = false var any = false
if (target.any { it.any { it is UserFilteredSlot && it.filter != null } }) { if (target.any { it.any { it.containerSlotOrNull() is IFilteredContainerSlot } }) {
for (collection in target) { for (collection in target) {
if (moveItemStackTo(slot, collection, onlyFiltered = true)) { if (moveItemStackTo(slot, collection, onlyFiltered = true)) {
any = true any = true
@ -582,9 +562,9 @@ abstract class MatteryMenu(
// first pass - stack with existing slots // first pass - stack with existing slots
if (copy.isStackable) { if (copy.isStackable) {
for (slot in slots) { for (slot in slots) {
if (onlyFiltered && (slot !is UserFilteredSlot || !slot.test(item))) { if (onlyFiltered && slot.containerSlotOrNull().let { it !is IFilteredContainerSlot || it.filter == null || !it.testSlotFilter(item) }) {
continue continue
} else if (!onlyFiltered && slot is UserFilteredSlot && !slot.test(item)) { } else if (!onlyFiltered && slot.containerSlotOrNull().let { it is IFilteredContainerSlot && it.filter != null }) {
continue continue
} }
@ -609,9 +589,9 @@ abstract class MatteryMenu(
// second pass - drop stack into first free slot // second pass - drop stack into first free slot
for (slot in slots) { for (slot in slots) {
if (onlyFiltered && (slot !is UserFilteredSlot || slot.filter == null || slot.filter!!.get() != item.item)) { if (onlyFiltered && slot.containerSlotOrNull().let { it !is IFilteredContainerSlot || it.filter == null || !it.testSlotFilter(item) }) {
continue continue
} else if (!onlyFiltered && slot is UserFilteredSlot && slot.filter != null && slot.filter!!.get() != null && slot.filter!!.get() != item.item) { } else if (!onlyFiltered && slot.containerSlotOrNull().let { it is IFilteredContainerSlot && it.filter != null }) {
continue continue
} }
@ -651,7 +631,7 @@ abstract class MatteryMenu(
val slot = slots[i] val slot = slots[i]
slots.add(slot) slots.add(slot)
if (slot is InventorySlot && slot.filter != null) { if (slot.containerSlotOrNull() is IFilteredContainerSlot) {
filters = true filters = true
} }
} }

View File

@ -17,20 +17,22 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.UpgradeType import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.capability.energy import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.container.IMatteryContainer import ru.dbotthepony.mc.otm.container.IMatteryContainer
import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.UpgradeContainer import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.container.util.containerSlotOrNull
import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.InstantBooleanInput import ru.dbotthepony.mc.otm.menu.input.InstantBooleanInput
import ru.dbotthepony.mc.otm.network.StreamCodecs
import ru.dbotthepony.mc.otm.runOnClient import ru.dbotthepony.mc.otm.runOnClient
import java.util.* import java.util.*
import java.util.function.BooleanSupplier import java.util.function.BooleanSupplier
import java.util.function.DoubleSupplier import java.util.function.DoubleSupplier
import java.util.function.IntSupplier import java.util.function.IntSupplier
import java.util.function.Predicate
import java.util.function.Supplier import java.util.function.Supplier
import kotlin.reflect.KMutableProperty0 import kotlin.reflect.KMutableProperty0
@ -51,6 +53,14 @@ inline fun <S : Slot> makeSlots(containers: List<Container>?, size: Int, initial
open class MatterySlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : Slot(container, index, x, y) { open class MatterySlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : Slot(container, index, x, y) {
var ignoreSpectators = true var ignoreSpectators = true
open fun setupNetworkControls(menu: MatteryMenu) {
val slot = containerSlotOrNull()
if (slot is IFilteredContainerSlot) {
menu.mSynchronizer.add(Delegate.Of(slot::filter), StreamCodecs.ITEM_TYPE_NULLABLE)
}
}
override fun setChanged() { override fun setChanged() {
if (container is IMatteryContainer) { if (container is IMatteryContainer) {
(container as IMatteryContainer).setChanged(containerSlot) (container as IMatteryContainer).setChanged(containerSlot)
@ -68,7 +78,7 @@ open class MatterySlot(container: Container, index: Int, x: Int = 0, y: Int = 0)
} }
open fun canTakeItemForPickAll(): Boolean { open fun canTakeItemForPickAll(): Boolean {
return true return (container.containerSlotOrNull(slotIndex) as? IFilteredContainerSlot)?.filter == null
} }
override fun getMaxStackSize(): Int { override fun getMaxStackSize(): Int {
@ -90,38 +100,29 @@ open class MatterySlot(container: Container, index: Int, x: Int = 0, y: Int = 0)
return super.getMaxStackSize(itemStack) return super.getMaxStackSize(itemStack)
} }
} }
}
open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y), Predicate<ItemStack> { private fun isSameFilter(other: Slot): Boolean {
var hasSetFilter = false val sSelf = containerSlotOrNull() as? IFilteredContainerSlot
private set val sOther = other.containerSlotOrNull() as? IFilteredContainerSlot
return sSelf?.filter == sOther?.filter
var filter: Delegate<Item?>? = null
set(value) {
hasSetFilter = true
field = value
}
override fun canTakeItemForPickAll(): Boolean {
return filter?.get() == null
}
override fun test(t: ItemStack): Boolean {
return filter?.get() == null || filter?.get() == t.item
}
fun isSameFilter(other: Slot): Boolean {
if (other !is UserFilteredSlot)
return filter?.get() == null
return (
(other.filter == null && filter == null) ||
(other.filter != null && filter != null && other.filter!!.get() == filter!!.get())
)
} }
override fun isSameInventory(other: Slot): Boolean { override fun isSameInventory(other: Slot): Boolean {
return isSameFilter(other) && super.isSameInventory(other) return super.isSameInventory(other) && isSameFilter(other)
}
}
open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
var filterInput: MatteryMenu.PlayerInput<Item?>? = null
private set
override fun setupNetworkControls(menu: MatteryMenu) {
super.setupNetworkControls(menu)
val slot = containerSlotOrNull()
if (slot is IFilteredContainerSlot) {
filterInput = menu.PlayerInput(StreamCodecs.ITEM_TYPE_NULLABLE, handler = { slot.filter = it })
}
} }
} }

View File

@ -5,6 +5,7 @@ import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.UserFilteredSlot import ru.dbotthepony.mc.otm.menu.UserFilteredSlot
import ru.dbotthepony.mc.otm.menu.makeSlots import ru.dbotthepony.mc.otm.menu.makeSlots
@ -15,7 +16,7 @@ class CargoCrateMenu(
inventory: Inventory, inventory: Inventory,
tile: CargoCrateBlockEntity? = null tile: CargoCrateBlockEntity? = null
) : MatteryMenu(MMenus.CARGO_CRATE, containerId, inventory, tile) { ) : MatteryMenu(MMenus.CARGO_CRATE, containerId, inventory, tile) {
val actualContainer: Container = tile?.container ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY) val actualContainer = tile?.container ?: SlottedContainer.filtered(CargoCrateBlockEntity.CAPACITY)
val storageSlots = makeSlots(actualContainer, ::UserFilteredSlot) val storageSlots = makeSlots(actualContainer, ::UserFilteredSlot)
private val trackedPlayerOpen = !inventory.player.isSpectator private val trackedPlayerOpen = !inventory.player.isSpectator