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.container.MatteryContainer
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.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
@ -43,20 +45,24 @@ class CargoCrateBlockEntity(
p_155229_: BlockPos,
p_155230_: BlockState
) : 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
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) {
unpackLootTable()
}
@ -88,7 +94,7 @@ class CargoCrateBlockEntity(
}
init {
exposeGlobally(Capabilities.ItemHandler.BLOCK, handler)
exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
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 {
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)
for (slot in menu.storageSlots)
UserFilteredSlotPanel.of(this, grid, slot)
UserFilteredSlotPanel(this, grid, slot)
val controls = DeviceControls(this, frame)

View File

@ -18,10 +18,6 @@ open class InventorySlotPanel<out S : MatteryScreen<*>, out T : MatteryMenu.Inve
x: Float = 0f,
y: Float = 0f,
) : 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) {
if (slot.chargeFlag?.get() == true) {
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 net.minecraft.ChatFormatting
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.network.chat.Component
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.Slot
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.minecraft
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.compat.itemborders.isItemBordersLoaded
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 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) {
slot.x = absoluteX.roundToInt() - screen.guiLeft
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 {
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)
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(

View File

@ -1,28 +1,16 @@
package ru.dbotthepony.mc.otm.client.screen.panels.slot
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.minecraft
import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
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.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.container.util.containerSlot
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,
parent: EditablePanel<*>?,
slot: T,
@ -31,77 +19,24 @@ abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
width: Float = SIZE,
height: Float = SIZE,
) : 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 {
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 (slotFilter === null) {
if (containerSlot.filter === null) {
if (screen.menu.carried.isEmpty) {
slotFilter = slot.item.item
slot.filterInput!!.accept(slot.item.item)
} else {
slotFilter = screen.menu.carried.item
slot.filterInput!!.accept(screen.menu.carried.item)
}
} else {
slotFilter = null
slot.filterInput!!.accept(null)
}
playGuiClickSound()
return true
} else {
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)
}
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)
for (slot in menu.storageSlots)
UserFilteredSlotPanel.of(this, grid, slot)
UserFilteredSlotPanel(this, grid, slot)
if (menu.isInput) {
val controls = DeviceControls(this, frame)

View File

@ -4,7 +4,7 @@ import net.minecraft.world.item.ItemStack
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 {
fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.container.util
import net.minecraft.world.Container
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
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> {
if (this is IEnhancedContainer) {
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.curios.curiosSlots
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.computeSortedIndices
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.collect.ConditionalSet
import ru.dbotthepony.mc.otm.core.math.Decimal
@ -207,7 +209,7 @@ abstract class MatteryMenu(
var sortInventoryInput: SortInput? = null
private set
val playerSortSettings = IItemStackSortingSettings.inputs(this, player.matteryPlayer?.sortingSettings)
val playerSortSettings = IItemStackSortingSettings.inputs(this, player.matteryPlayer.sortingSettings)
var offhandSlot: InventorySlot? = null
protected set
@ -221,7 +223,7 @@ abstract class MatteryMenu(
protected var inventorySlotIndexStart = 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 {
return !isInventorySlotLocked(index) && super.mayPlace(itemStack)
}
@ -236,15 +238,6 @@ abstract class MatteryMenu(
init {
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) {
chargeFlag = Delegate.Of(
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) {
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)
@ -392,21 +385,8 @@ abstract class MatteryMenu(
if (!seenSlots.add(pSlot))
return pSlot
if (pSlot is UserFilteredSlot && !pSlot.hasSetFilter) {
val container = pSlot.container
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)
if (pSlot is MatterySlot) {
pSlot.setupNetworkControls(this)
}
return super.addSlot(pSlot)
@ -487,7 +467,7 @@ abstract class MatteryMenu(
val copy = slot.item.copy()
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) {
if (moveItemStackTo(slot, collection, onlyFiltered = true)) {
any = true
@ -582,9 +562,9 @@ abstract class MatteryMenu(
// first pass - stack with existing slots
if (copy.isStackable) {
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
} else if (!onlyFiltered && slot is UserFilteredSlot && !slot.test(item)) {
} else if (!onlyFiltered && slot.containerSlotOrNull().let { it is IFilteredContainerSlot && it.filter != null }) {
continue
}
@ -609,9 +589,9 @@ abstract class MatteryMenu(
// second pass - drop stack into first free slot
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
} 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
}
@ -651,7 +631,7 @@ abstract class MatteryMenu(
val slot = slots[i]
slots.add(slot)
if (slot is InventorySlot && slot.filter != null) {
if (slot.containerSlotOrNull() is IFilteredContainerSlot) {
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.energy
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.ItemFilter
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.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.InstantBooleanInput
import ru.dbotthepony.mc.otm.network.StreamCodecs
import ru.dbotthepony.mc.otm.runOnClient
import java.util.*
import java.util.function.BooleanSupplier
import java.util.function.DoubleSupplier
import java.util.function.IntSupplier
import java.util.function.Predicate
import java.util.function.Supplier
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) {
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() {
if (container is IMatteryContainer) {
(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 {
return true
return (container.containerSlotOrNull(slotIndex) as? IFilteredContainerSlot)?.filter == null
}
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)
}
}
}
open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y), Predicate<ItemStack> {
var hasSetFilter = false
private set
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())
)
private fun isSameFilter(other: Slot): Boolean {
val sSelf = containerSlotOrNull() as? IFilteredContainerSlot
val sOther = other.containerSlotOrNull() as? IFilteredContainerSlot
return sSelf?.filter == sOther?.filter
}
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.Player
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.UserFilteredSlot
import ru.dbotthepony.mc.otm.menu.makeSlots
@ -15,7 +16,7 @@ class CargoCrateMenu(
inventory: Inventory,
tile: CargoCrateBlockEntity? = null
) : 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)
private val trackedPlayerOpen = !inventory.player.isSpectator