From d4c029f27d5249f9178228715ce27885962ee4cf Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 20 Aug 2023 15:39:20 +0700 Subject: [PATCH] Slice container APIs and decouple interface from implementation, container sorting test --- .../mc/otm/datagen/lang/English.kt | 1 + .../mc/otm/datagen/lang/Russian.kt | 1 + .../entity/storage/ItemMonitorBlockEntity.kt | 4 +- .../entity/tech/PlatePressBlockEntity.kt | 3 +- .../ru/dbotthepony/mc/otm/capability/Ext.kt | 41 +-- .../otm/capability/MatteryPlayerCapability.kt | 262 +++++------------- .../mc/otm/client/ClientEventHandler.kt | 10 +- .../mc/otm/client/render/WidgetLocation.kt | 2 +- .../mc/otm/client/render/Widgets18.kt | 3 +- .../client/screen/ExopackInventoryScreen.kt | 21 +- .../mc/otm/client/screen/MatteryScreen.kt | 25 +- .../screen/decorative/CargoCrateScreen.kt | 8 + .../client/screen/decorative/PainterScreen.kt | 2 +- .../client/screen/matter/MatterPanelScreen.kt | 2 +- .../client/screen/panels/button/Buttons.kt | 28 +- .../button/LargeRectangleButtonPanel.kt | 34 ++- .../screen/tech/EssenceStorageScreen.kt | 18 +- .../mc/otm/container/CombinedContainer.kt | 130 +++++---- .../mc/otm/container/ContainerHandler.kt | 4 +- .../mc/otm/container/ContainerHelpers.kt | 77 ++++- .../container/DynamicallyProxiedContainer.kt | 81 ++++++ .../mc/otm/container/IContainer.kt | 11 + .../mc/otm/container/IMatteryContainer.kt | 217 +++++++++++++++ .../mc/otm/container/MatteryContainer.kt | 199 ++++--------- .../mc/otm/container/util/Iterators.kt | 75 +++-- .../kotlin/ru/dbotthepony/mc/otm/core/Ext.kt | 13 +- .../mc/otm/core/collect/Iterables.kt | 24 ++ .../mc/otm/core/collect/StreamyIterators.kt | 4 +- .../mc/otm/core/util/ItemSorter.kt | 28 +- .../mc/otm/item/QuantumBatteryItem.kt | 4 +- .../ru/dbotthepony/mc/otm/menu/MatteryMenu.kt | 114 +++----- .../ru/dbotthepony/mc/otm/menu/Slots.kt | 22 ++ .../mc/otm/menu/data/NetworkedItemView.kt | 2 +- .../mc/otm/menu/decorative/CargoCrateMenu.kt | 7 +- .../mc/otm/menu/storage/DriveViewerMenu.kt | 2 +- .../mc/otm/menu/storage/ItemMonitorMenu.kt | 2 +- .../textures/gui/widgets/storage_controls.png | Bin 1020 -> 1063 bytes .../textures/gui/widgets/storage_controls.xcf | Bin 9801 -> 10269 bytes 38 files changed, 845 insertions(+), 636 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/container/DynamicallyProxiedContainer.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/container/IMatteryContainer.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/Iterables.kt diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt index cbc045636..980212cbc 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt @@ -805,6 +805,7 @@ private fun gui(provider: MatteryLanguageProvider) { gui("balance_inputs", "Balance input slots") + gui("sorting.sort_now", "Sort") gui("sorting.default", "Default sorting") gui("sorting.name", "Sort by name") gui("sorting.id", "Sort by ID") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt index 0bd7a8786..f9711bcfb 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt @@ -805,6 +805,7 @@ private fun gui(provider: MatteryLanguageProvider) { gui("balance_inputs", "Балансировать входные слоты") + gui("sorting.sort_now", "Отсортировать") gui("sorting.default", "Сортировка по умолчанию") gui("sorting.name", "Сортировка по имени") gui("sorting.id", "Сортировка по ID") diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/ItemMonitorBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/ItemMonitorBlockEntity.kt index a9a2be2da..7f3a1aace 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/ItemMonitorBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/storage/ItemMonitorBlockEntity.kt @@ -43,8 +43,6 @@ import java.util.* import kotlin.collections.HashMap import ru.dbotthepony.mc.otm.client.render.Widgets8 import ru.dbotthepony.mc.otm.container.CombinedContainer -import ru.dbotthepony.mc.otm.container.addItem -import ru.dbotthepony.mc.otm.container.util.iterator import ru.dbotthepony.mc.otm.container.util.slotIterator import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.toList @@ -345,7 +343,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte check(residue.size == craftingGrid.containerSize) { "Container and residue list sizes mismatch: ${residue.size} != ${craftingGrid.containerSize}" } - val combinedInventory = craftingPlayer.matteryPlayer?.combinedInventory + val combinedInventory = craftingPlayer.matteryPlayer?.inventoryAndExopack val copy = craftingGrid.iterator(true).map { it.copy() }.toList() // удаляем по одному предмету из сетки крафта diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PlatePressBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PlatePressBlockEntity.kt index 3c2e7368d..5dcf9e6e0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PlatePressBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/PlatePressBlockEntity.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.mc.otm.block.entity.tech +import it.unimi.dsi.fastutil.ints.IntArrayList import net.minecraft.core.BlockPos import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerPlayer @@ -74,7 +75,7 @@ class PlatePressBlockEntity( if (status.job.itemStack.isEmpty) return status.success() - if (!outputContainer.fullyAddItem(status.job.itemStack, start = id, end = id) && !outputContainer.fullyAddItem(status.job.itemStack)) + if (!outputContainer.fullyAddItem(status.job.itemStack, slots = IntArrayList.of(id)) && !outputContainer.fullyAddItem(status.job.itemStack)) return status.noItem() experience = (experience + status.experience).coerceAtMost(100.0) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt index b22b6cef7..55e10351a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt @@ -1,7 +1,6 @@ package ru.dbotthepony.mc.otm.capability import com.google.common.collect.Streams -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet import net.minecraft.ChatFormatting import net.minecraft.core.Direction import net.minecraft.network.chat.Component @@ -27,11 +26,11 @@ import ru.dbotthepony.mc.otm.compat.curios.isCuriosLoaded import ru.dbotthepony.mc.otm.compat.mekanism.getMekanismEnergySided import ru.dbotthepony.mc.otm.compat.mekanism.mekanismEnergy import ru.dbotthepony.mc.otm.container.util.awareStream -import ru.dbotthepony.mc.otm.capability.iterator import ru.dbotthepony.mc.otm.container.util.iterator import ru.dbotthepony.mc.otm.core.collect.AwareItemStack import ru.dbotthepony.mc.otm.core.collect.ContainerItemStackEntry import ru.dbotthepony.mc.otm.core.collect.concatIterators +import ru.dbotthepony.mc.otm.core.collect.emptyIterator import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.isNotEmpty @@ -217,21 +216,16 @@ fun ICapabilityProvider.getMatteryEnergySided(side: Direction? = null): LazyOpti return LazyOptional.empty() } -/** - * DO NOT modify returned ItemStacks! - * - * Contains all items that player might carry - */ fun Player.items(includeCosmetics: Boolean = true): Iterator { - val iterators = ArrayList>() - iterators.add(inventory.iterator()) + val matteryPlayer = matteryPlayer ?: return emptyIterator() - matteryPlayer?.let { - if (it.hasExopack) { - iterators.add(it.exopackContainer.iterator()) - iterators.add(it.exopackEnergy.parent.iterator()) - iterators.add(it.exopackChargeSlots.iterator()) - } + val iterators = ArrayList>() + iterators.add(matteryPlayer.wrappedInventory.slotIterator().filter { !it.isForbiddenForAutomation }.map { it.item }) + + if (matteryPlayer.hasExopack) { + iterators.add(matteryPlayer.exopackContainer.slotIterator().filter { !it.isForbiddenForAutomation }.map { it.item }) + iterators.add(matteryPlayer.exopackEnergy.parent.iterator()) + iterators.add(matteryPlayer.exopackChargeSlots.iterator()) } if (isCuriosLoaded) { @@ -245,25 +239,12 @@ fun Player.items(includeCosmetics: Boolean = true): Iterator { return concatIterators(iterators) } -/** - * DO NOT modify returned ItemStacks! - * - * Contains all items that player might see/access ([itemsStream] + open container's items) - */ -fun Player.allItems(includeCosmetics: Boolean = true): Iterator { +fun Player.trackedItems(includeCosmetics: Boolean = true): Iterator { if (containerMenu == inventoryMenu || containerMenu == matteryPlayer?.exoPackMenu) { return items(includeCosmetics) } - val seen = ReferenceOpenHashSet(containerMenu.slots.size) - - for (slot in containerMenu.slots) { - seen.add(slot.item) - } - - return concatIterators( - items(includeCosmetics).filter { it.isNotEmpty && it !in seen }, - containerMenu.slots.iterator().map { it.item }) + return concatIterators(items(includeCosmetics), containerMenu.slots.iterator().map { it.item }.filter { it.isNotEmpty }) } /** diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt index fc6fc98d5..423738677 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt @@ -10,7 +10,6 @@ import net.minecraft.client.player.AbstractClientPlayer import net.minecraft.commands.Commands import net.minecraft.commands.arguments.EntityArgument import net.minecraft.core.Direction -import net.minecraft.nbt.ByteTag import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.IntTag import net.minecraft.nbt.ListTag @@ -31,6 +30,7 @@ import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.boss.wither.WitherBoss import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items import net.minecraft.world.item.ProjectileWeaponItem @@ -82,7 +82,9 @@ import ru.dbotthepony.mc.otm.config.ExopackConfig import ru.dbotthepony.mc.otm.container.CombinedContainer import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.get -import ru.dbotthepony.mc.otm.capability.iterator +import ru.dbotthepony.mc.otm.container.DynamicallyProxiedContainer +import ru.dbotthepony.mc.otm.container.IContainer +import ru.dbotthepony.mc.otm.container.IMatteryContainer import ru.dbotthepony.mc.otm.container.util.slotIterator import ru.dbotthepony.mc.otm.container.vanishCursedItems import ru.dbotthepony.mc.otm.core.* @@ -91,7 +93,6 @@ import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.RGBAColor import ru.dbotthepony.mc.otm.core.math.minus -import ru.dbotthepony.mc.otm.core.nbt.getByteList import ru.dbotthepony.mc.otm.core.nbt.getCompoundList import ru.dbotthepony.mc.otm.core.nbt.getIntList import ru.dbotthepony.mc.otm.core.nbt.getStringList @@ -258,27 +259,23 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial synchronizer.Field(null, ItemValueCodec.nullable) } - val regularSlotChargeFlag = immutableList(Inventory.INVENTORY_SIZE + 5) { - synchronizer.bool() - } + val slotsChargeFlag by synchronizer.Set( + codec = VarIntValueCodec, + backingSet = IntAVLTreeSet(), + ) private fun slotChargeToDefault() { // броня - regularSlotChargeFlag[36].boolean = true - regularSlotChargeFlag[37].boolean = true - regularSlotChargeFlag[38].boolean = true - regularSlotChargeFlag[39].boolean = true + slotsChargeFlag.add(36) + slotsChargeFlag.add(37) + slotsChargeFlag.add(38) + slotsChargeFlag.add(39) } init { slotChargeToDefault() } - val exoPackSlotsChargeFlag by synchronizer.Set( - codec = VarIntValueCodec, - backingSet = IntAVLTreeSet(), - ) - /** * Exopack container, which actually store items inside Exopack */ @@ -303,19 +300,60 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial value.addFilterSynchronizer(synchronizer) field = value - _combinedInventory = CombinedContainer(ply.inventory, exopackContainer) + _combinedInventory = CombinedContainer(wrappedInventory, exopackContainer) + _combinedInventory2 = CombinedContainer(wrappedItemInventory, exopackContainer) + _combinedInventory3 = CombinedContainer.Builder().add(wrappedItemInventory, 9 .. 35).add(exopackContainer).build() } private var _combinedInventory: CombinedContainer? = null + private var _combinedInventory2: CombinedContainer? = null + private var _combinedInventory3: CombinedContainer? = null + + val wrappedInventory: IMatteryContainer = object : IMatteryContainer, IContainer by DynamicallyProxiedContainer(ply::getInventory) { + override fun getSlotFilter(slot: Int): Item? { + return regularSlotFilters.getOrNull(slot)?.get() + } + + override fun setSlotFilter(slot: Int, filter: Item?): Boolean { + regularSlotFilters.getOrNull(slot)?.accept(filter) + return true + } + + override fun setChanged(slot: Int) { + ply.inventory.setChanged() + } + } + + val wrappedItemInventory: IMatteryContainer = object : IMatteryContainer by wrappedInventory { + override fun getContainerSize(): Int { + return 36 + } + } val combinedInventory: CombinedContainer get() { if (_combinedInventory == null) - _combinedInventory = CombinedContainer(ply.inventory, exopackContainer) + _combinedInventory = CombinedContainer(wrappedInventory, exopackContainer) return _combinedInventory!! } + val inventoryAndExopack: CombinedContainer + get() { + if (_combinedInventory2 == null) + _combinedInventory2 = CombinedContainer(wrappedItemInventory, exopackContainer) + + return _combinedInventory2!! + } + + val inventoryAndExopackNoHotbar: CombinedContainer + get() { + if (_combinedInventory3 == null) + _combinedInventory3 = CombinedContainer.Builder().add(wrappedItemInventory, 9 .. 35).add(exopackContainer).build() + + return _combinedInventory3!! + } + /** * Whenever Exopack has 3x3 crafting grid upgrade installed */ @@ -914,14 +952,8 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial } } - tag["regularSlotChargeFlag"] = ListTag().also { - for (flag in regularSlotChargeFlag) { - it.add(ByteTag.valueOf(flag.boolean)) - } - } - - tag["exoPackSlotsChargeFlag"] = ListTag().also { - for (value in exoPackSlotsChargeFlag) { + tag["slotsChargeFlag"] = ListTag().also { + for (value in slotsChargeFlag) { it.add(IntTag.valueOf(value)) } } @@ -945,12 +977,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial filter.value = null } - for (flag in regularSlotChargeFlag) { - flag.boolean = false - } - - exoPackSlotsChargeFlag.clear() - + slotsChargeFlag.clear() slotChargeToDefault() val regularSlotFilters = tag.getStringList("regularSlotFilters") @@ -961,14 +988,8 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial this.regularSlotFilters[i].value = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(path) ?: continue) ?: Items.AIR } - val regularSlotChargeFlag = tag.getByteList("regularSlotChargeFlag") - - for (i in 0 until regularSlotChargeFlag.size.coerceAtMost(this.regularSlotChargeFlag.size)) { - this.regularSlotChargeFlag[i].boolean = regularSlotChargeFlag[i].asInt > 0 - } - - for (v in tag.getIntList("exoPackSlotsChargeFlag")) { - this.exoPackSlotsChargeFlag.add(v.asInt) + for (v in tag.getIntList("slotsChargeFlag")) { + this.slotsChargeFlag.add(v.asInt) } // iterations @@ -1128,25 +1149,9 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial } if (available.isPositive) { - for ((i, flag) in regularSlotChargeFlag.withIndex()) { - if (flag.boolean) { - val item = ply.inventory[i] - - if (item.isNotEmpty) { - item.energy?.let { - available -= exopackEnergy.extractEnergy(it.receiveEnergy(available, false), false) - } - - if (!available.isPositive) break - } - } - } - } - - if (available.isPositive) { - for (slot in exoPackSlotsChargeFlag) { - if (slot in 0 until exopackContainer.containerSize) { - val item = exopackContainer[slot] + for (slot in slotsChargeFlag) { + if (slot in 0 until combinedInventory.containerSize) { + val item = combinedInventory[slot] if (item.isNotEmpty) { item.energy?.let { @@ -1364,127 +1369,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial * This re-implement [Inventory.add] logic (original method is redirected to this) */ fun inventoryAddImpl(stack: ItemStack): Boolean { - val items = ply.inventory.items - val exoPackContainer = exopackContainer - - if (!stack.isStackable) { - // двигаем в отфильтрованные слоты - for (i in items.indices) { - if (items[i].isEmpty && regularSlotFilters[i].value === stack.item) { - items[i] = stack.copy() - items[i].popTime = 5 - stack.count = 0 - return true - } - } - - for (i in 0 until exoPackContainer.containerSize) { - if (exoPackContainer[i].isEmpty && exoPackContainer.hasSlotFilter(i) && exoPackContainer.testSlotFilter(i, stack)) { - exoPackContainer[i] = stack.copy() - exoPackContainer[i].popTime = 5 - stack.count = 0 - return true - } - } - - // двигаем в обычные слоты - for (i in items.indices) { - if (items[i].isEmpty && regularSlotFilters[i].value === null) { - items[i] = stack.copy() - items[i].popTime = 5 - stack.count = 0 - return true - } - } - - for (i in 0 until exoPackContainer.containerSize) { - if (exoPackContainer[i].isEmpty && !exoPackContainer.hasSlotFilter(i)) { - exoPackContainer[i] = stack.copy() - exoPackContainer[i].popTime = 5 - stack.count = 0 - return true - } - } - - MinecraftForge.EVENT_BUS.post(ItemStackLeftoverEvent(stack, this)) - - if (ply.abilities.instabuild) { - stack.count = 0 - } - - return stack.isEmpty - } - - // двигаем в существующие слоты - items[ply.inventory.selected].also { stackStacks(it, stack) } - if (stack.isEmpty) return true - ply.inventory.offhand[0].also { stackStacks(it, stack) } - if (stack.isEmpty) return true - - for (i in items.indices) { - if (stackStacks(items[i], stack) && stack.isEmpty) { - return true - } - } - - for (i in 0 until exoPackContainer.containerSize) { - if (stackStacks(exoPackContainer[i], stack)) { - exoPackContainer.setChanged(i) - - if (stack.isEmpty) { - return true - } - } - } - - // двигаем в пустые слоты - - // двигаем в отфильтрованные слоты - for (i in items.indices) { - if (items[i].isEmpty && regularSlotFilters[i].value === stack.item) { - items[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize)) - items[i].popTime = 5 - - if (stack.isEmpty) { - return true - } - } - } - - for (i in 0 until exoPackContainer.containerSize) { - if (exoPackContainer[i].isEmpty && exoPackContainer.hasSlotFilter(i) && exoPackContainer.testSlotFilter(i, stack)) { - exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize)) - exoPackContainer[i].popTime = 5 - - if (stack.isEmpty) { - return true - } - } - } - - // двигаем в обычные слоты - for (i in items.indices) { - if (items[i].isEmpty && regularSlotFilters[i].value === null) { - items[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize)) - items[i].popTime = 5 - - if (stack.isEmpty) { - return true - } - } - } - - for (i in 0 until exoPackContainer.containerSize) { - if (exoPackContainer[i].isEmpty && !exoPackContainer.hasSlotFilter(i)) { - exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize)) - exoPackContainer[i].popTime = 5 - - if (stack.isEmpty) { - return true - } - } - } - + inventoryAndExopack.consumeItem(stack, false, popTime = 5) MinecraftForge.EVENT_BUS.post(ItemStackLeftoverEvent(stack, this)) if (ply.abilities.instabuild) { @@ -1587,25 +1472,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial ) } - private fun stackStacks(it: ItemStack, stack: ItemStack): Boolean { - if ( - !it.isEmpty && - it.maxStackSize > it.count && - it.isStackable && - ItemStack.isSameItemSameTags(it, stack) - ) { - val new = (it.count + stack.count).coerceAtMost(it.maxStackSize) - val diff = new - it.count - it.count = new - stack.count -= diff - it.popTime = 5 - - return true - } - - return false - } - init { WitherBoss.TARGETING_CONDITIONS.selector(WitherBoss.TARGETING_CONDITIONS.selector!!.and { it.matteryPlayer?.isAndroid != true }) WitherBoss.LIVING_ENTITY_SELECTOR = WitherBoss.LIVING_ENTITY_SELECTOR.and { it.matteryPlayer?.isAndroid != true } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt index 10cdb96b9..ae7c9b1de 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt @@ -85,11 +85,11 @@ private fun inventoryLogic(event: ScreenEvent.Init.Post) { val screen = if (eventScreen is AbstractContainerScreen<*> && (eventScreen.menu is InventoryMenu || eventScreen.isCosmeticArmorScreen)) eventScreen else return val widget = Panel2Widget(LargeRectangleButtonPanel(screen, null, - x = screen.guiLeft + screen.xSize - 2f - LargeRectangleButtonPanel.SIZE, - y = screen.guiTop.toFloat() - LargeRectangleButtonPanel.SIZE - 2, - skinElement = Widgets18.RETURN_ARROW_LEFT, - skinElementWinding = UVWindingOrder.FLOP, - onPress = { + x = screen.guiLeft + screen.xSize - 2f - LargeRectangleButtonPanel.SIZE, + y = screen.guiTop.toFloat() - LargeRectangleButtonPanel.SIZE - 2, + icon = Widgets18.RETURN_ARROW_LEFT, + iconWinding = UVWindingOrder.FLOP, + onPress = { shouldOpenVanillaInventory = false val mouseX = minecraft.mouseHandler.xpos() val mouseY = minecraft.mouseHandler.ypos() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetLocation.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetLocation.kt index 9da40a685..7929fc86c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetLocation.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetLocation.kt @@ -6,7 +6,7 @@ import ru.dbotthepony.mc.otm.client.render.sprites.MatteryAtlas object WidgetLocation { val LARGE_BUTTON = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/large_button.png"), 72f, 18f) - val STORAGE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/storage_controls.png"), 90f, 54f) + val STORAGE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/storage_controls.png"), 90f, 72f) val MISC_18 = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/misc18.png"), 72f, 72f) val SLOT_BACKGROUNDS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/slot_backgrounds.png"), 72f, 72f) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt index d25039af3..60849bf3a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt @@ -37,7 +37,7 @@ object Widgets18 { val BUTTON_DISABLED_STRETCHABLE = makeButton(buttonGrids) val BUTTON_DISABLED = buttonGrids.next() - private val storageGrid = WidgetLocation.STORAGE_CONTROLS.grid(rows = 3, columns = 5) + private val storageGrid = WidgetLocation.STORAGE_CONTROLS.grid(rows = 4, columns = 5) val ARROW_DOWN = storageGrid.next() val ARROW_UP = storageGrid.next() val SORT_DEFAULT = storageGrid.next() @@ -53,6 +53,7 @@ object Widgets18 { val PAUSE = storageGrid.next() val PLAY = storageGrid.next() val STOP = storageGrid.next() + val SORT_NOW = storageGrid.next() private val miscGrid = WidgetLocation.MISC_18.grid(4, 4) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt index 2dad7fb4e..5a43c252d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt @@ -10,6 +10,7 @@ import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.client.render.sprites.sprite import ru.dbotthepony.mc.otm.client.screen.panels.* +import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.InventorySlotPanel @@ -243,7 +244,7 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen + val controls = DeviceControls(this, frame) + + controls.dockTop = 85f + + controls.addButton(makeInventoryRowsControls(frame) { movePixels -> frame.y += movePixels moveMousePosScaled(y = movePixels) + } as EditablePanel) + + + if (menu.sortInventoryInput != null) { + controls.addButton(LargeRectangleButtonPanel.input( + this, + controls, + menu.sortInventoryInput!!, + icon = Widgets18.SORT_NOW + ).also { it.tooltips.add(TranslatableComponent("otm.gui.sorting.sort_now")) }) } var x = -4f diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt index a3803ee63..df07050f1 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt @@ -22,6 +22,8 @@ import ru.dbotthepony.mc.otm.client.render.WidgetLocation import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.translation import ru.dbotthepony.mc.otm.client.screen.panels.* +import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls +import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.InventorySlotPanel @@ -157,7 +159,7 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit inventoryScrollbar.scroll = inventoryScrollbar.scroll } - protected fun makeInventoryRowsControls(parent: EditablePanel<*>, x: Float, y: Float, callback: (Float) -> Unit): HeightControls<*> { + protected fun makeInventoryRowsControls(parent: EditablePanel<*>, x: Float = 0f, y: Float = 0f, callback: (Float) -> Unit): HeightControls> { return HeightControls(this, parent, x, y) { inventoryRows += if (it) 1 else -1 callback.invoke(if (it) -AbstractSlotPanel.SIZE / 2f else AbstractSlotPanel.SIZE / 2f) @@ -206,13 +208,17 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit init { if (menu.playerInventorySlots.isNotEmpty() && menu.autoCreateInventoryFrame) { - if (menu.playerExoSuitSlots.isEmpty()) { + val deviceControls: DeviceControls> + + if (menu.playerCombinedInventorySlots.size <= 27) { inventoryFrame = FramePanel>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH, INVENTORY_FRAME_HEIGHT, inventory.displayName).also(this::addPanel) inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().also { if (menu.player.matteryPlayer?.hasExopack == true) it.tooltips.add(TranslatableComponent("otm.gui.help.slot_charging")) } + deviceControls = DeviceControls(this, inventoryFrame!!) + val hotbarStrip = EditablePanel(this, inventoryFrame, height = AbstractSlotPanel.SIZE) hotbarStrip.dock = Dock.BOTTOM @@ -240,6 +246,8 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit it.tooltips.add(TranslatableComponent("otm.gui.help.slot_charging")) } + deviceControls = DeviceControls(this, inventoryFrame!!) + inventoryScrollbar = DiscreteScrollBarPanel(this, inventoryFrame, { integerDivisionDown(menu.playerCombinedInventorySlots.size, 9) }, { _, old, new -> @@ -292,11 +300,20 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit } } - makeInventoryRowsControls(inventoryFrame!!, inventoryFrame!!.width + 2f, 0f) { movePixels -> + deviceControls.addButton(makeInventoryRowsControls(inventoryFrame!!) { movePixels -> mainFrame?.let { it.y += movePixels } inventoryFrame?.let { it.y += movePixels } moveMousePosScaled(y = movePixels) - } + }) + } + + if (menu.sortInventoryInput != null) { + deviceControls.addButton(LargeRectangleButtonPanel.input( + this, + deviceControls, + menu.sortInventoryInput!!, + icon = Widgets18.SORT_NOW + ).also { it.tooltips.add(TranslatableComponent("otm.gui.sorting.sort_now")) }) } if (menu.exopackChargeSlots.isNotEmpty()) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/CargoCrateScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/CargoCrateScreen.kt index 6aeaa70e4..99d4e560d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/CargoCrateScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/CargoCrateScreen.kt @@ -2,11 +2,15 @@ package ru.dbotthepony.mc.otm.client.screen.decorative import net.minecraft.network.chat.Component import net.minecraft.world.entity.player.Inventory +import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel +import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls +import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel +import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Component) : MatteryScreen(menu, inventory, title) { @@ -22,6 +26,10 @@ class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Compon for (slot in menu.storageSlots) UserFilteredSlotPanel.of(this, grid, slot) + val controls = DeviceControls(this, frame) + + controls.addButton(LargeRectangleButtonPanel.input(this, frame, menu.sort, icon = Widgets18.SORT_NOW).also { it.tooltips.add(TranslatableComponent("otm.gui.sorting.sort_now")) }) + return frame } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/PainterScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/PainterScreen.kt index 8ef2980bd..11e2820ea 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/PainterScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/decorative/PainterScreen.kt @@ -110,7 +110,7 @@ class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) : buttons.clear() for (recipe in menu.possibleRecipes) { - object : LargeRectangleButtonPanel(this@PainterScreen, canvas.canvas, skinElement = ItemStackIcon(recipe.output, 14f, 14f).fixed()) { + object : LargeRectangleButtonPanel(this@PainterScreen, canvas.canvas, icon = ItemStackIcon(recipe.output, 14f, 14f).fixed()) { init { buttons.add(this) dockRight = 1f diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterPanelScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterPanelScreen.kt index 89829584b..b4b4a2127 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterPanelScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/matter/MatterPanelScreen.kt @@ -77,7 +77,7 @@ class MatterPanelScreen( LargeRectangleButtonPanel( this, frame, - skinElement = Widgets18.STOP, + icon = Widgets18.STOP, onPress = { frame.queryUser( TranslatableComponent("otm.gui.matter_panel.cancel_all"), diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt index 08a59178e..853c876a1 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt @@ -386,7 +386,7 @@ class DeviceControls>( if (upgrades != null) { upgradesButton = addButton(object : LargeRectangleButtonPanel( screen, this@DeviceControls, - skinElement = Widgets18.UPGRADES + icon = Widgets18.UPGRADES ) { init { tooltips.add(TranslatableComponent("otm.gui.upgrades")) @@ -473,7 +473,7 @@ class DeviceControls>( } if (itemConfig != null) { - itemConfigButton = addButton(object : LargeRectangleButtonPanel(screen, this@DeviceControls, skinElement = Widgets18.ITEMS_CONFIGURATION) { + itemConfigButton = addButton(object : LargeRectangleButtonPanel(screen, this@DeviceControls, icon = Widgets18.ITEMS_CONFIGURATION) { init { tooltips.add(TranslatableComponent("otm.gui.sides.item_config")) } @@ -492,7 +492,7 @@ class DeviceControls>( } if (energyConfig != null) { - energyConfigButton = addButton(object : LargeRectangleButtonPanel(screen, this@DeviceControls, y = nextY, skinElement = Widgets18.ENERGY_CONFIGURATION) { + energyConfigButton = addButton(object : LargeRectangleButtonPanel(screen, this@DeviceControls, y = nextY, icon = Widgets18.ENERGY_CONFIGURATION) { init { tooltips.add(TranslatableComponent("otm.gui.sides.energy_config")) } @@ -511,7 +511,7 @@ class DeviceControls>( } if (fluidConfig != null) { - fluidConfigButton = addButton(object : LargeRectangleButtonPanel(screen, this@DeviceControls, y = nextY, skinElement = Widgets18.FLUID_CONFIGURATION) { + fluidConfigButton = addButton(object : LargeRectangleButtonPanel(screen, this@DeviceControls, y = nextY, icon = Widgets18.FLUID_CONFIGURATION) { init { tooltips.add(TranslatableComponent("otm.gui.sides.fluid_config")) } @@ -530,10 +530,28 @@ class DeviceControls>( } } + override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + x = (parent?.width ?: 0f) + 3f + y = dockTop + } + override fun tickInner() { super.tickInner() x = (parent?.width ?: 0f) + 3f - y = 0f + y = dockTop + } + + // не съедаем ввод мыши + override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { + return false + } + + override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean { + return false + } + + override fun mouseDraggedInner(x: Double, y: Double, button: Int, xDelta: Double, yDelta: Double): Boolean { + return false } companion object { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/LargeRectangleButtonPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/LargeRectangleButtonPanel.kt index 20d14a49f..5c1e22d90 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/LargeRectangleButtonPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/LargeRectangleButtonPanel.kt @@ -2,10 +2,12 @@ package ru.dbotthepony.mc.otm.client.screen.panels.button import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.screens.Screen +import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.render.IGUIRenderable import ru.dbotthepony.mc.otm.client.render.UVWindingOrder import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel +import ru.dbotthepony.mc.otm.menu.MatteryMenu open class LargeRectangleButtonPanel( screen: S, @@ -15,8 +17,8 @@ open class LargeRectangleButtonPanel( width: Float = SIZE, height: Float = SIZE, onPress: ((clickButton: Int) -> Unit)? = null, - var skinElement: IGUIRenderable? = null, - var skinElementWinding: UVWindingOrder? = null, + var icon: IGUIRenderable? = null, + var iconWinding: UVWindingOrder? = null, ) : RectangleButtonPanel(screen, parent, x, y, width, height, onPress) { final override val IDLE = Widgets18.BUTTON_IDLE final override val HOVERED = Widgets18.BUTTON_HOVERED @@ -26,14 +28,36 @@ open class LargeRectangleButtonPanel( override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { super.innerRender(graphics, mouseX, mouseY, partialTick) - if (skinElementWinding != null) { - skinElement?.render(graphics, width = width, height = height, winding = skinElementWinding!!) + if (iconWinding != null) { + icon?.render(graphics, width = width, height = height, winding = iconWinding!!) } else { - skinElement?.render(graphics, width = width, height = height) + icon?.render(graphics, width = width, height = height) } } companion object { const val SIZE = 18f + + fun input( + screen: S, + parent: EditablePanel<*>?, + input: MatteryMenu.PlayerInput, + x: Float = 0f, + y: Float = 0f, + width: Float = SIZE, + height: Float = SIZE, + icon: IGUIRenderable? = null, + iconWinding: UVWindingOrder? = null, + ): LargeRectangleButtonPanel { + return object : LargeRectangleButtonPanel(screen, parent, x, y, width, height, null, icon, iconWinding) { + override fun onClick(mouseButton: Int) { + input.accept(null) + } + + override var isDisabled: Boolean + get() = !input.test(minecraft.player) + set(value) {} + } + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EssenceStorageScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EssenceStorageScreen.kt index b7d3b7a2e..1fe7a1f46 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EssenceStorageScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EssenceStorageScreen.kt @@ -93,7 +93,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title outputs.dockResize = DockResizeMode.NONE outputs.dockMargin = DockProperty(bottom = 3f) - object : LargeRectangleButtonPanel(this@EssenceStorageScreen, inputs, skinElement = STORE_1) { + object : LargeRectangleButtonPanel(this@EssenceStorageScreen, inputs, icon = STORE_1) { init { dockRight = 3f tooltips.add(TranslatableComponent("otm.gui.experience.store", 1)) @@ -108,7 +108,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title set(value) {} } - object : LargeRectangleButtonPanel(this@EssenceStorageScreen, inputs, skinElement = STORE_10) { + object : LargeRectangleButtonPanel(this@EssenceStorageScreen, inputs, icon = STORE_10) { init { dockRight = 3f tooltips.add(TranslatableComponent("otm.gui.experience.store", 10)) @@ -123,7 +123,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title set(value) {} } - object : LargeRectangleButtonPanel(this@EssenceStorageScreen, inputs, skinElement = STORE_ALL) { + object : LargeRectangleButtonPanel(this@EssenceStorageScreen, inputs, icon = STORE_ALL) { init { dockRight = 3f tooltips.add(TranslatableComponent("otm.gui.experience.store_all")) @@ -138,7 +138,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title set(value) {} } - object : LargeRectangleButtonPanel(this@EssenceStorageScreen, outputs, skinElement = DISPENSE_1) { + object : LargeRectangleButtonPanel(this@EssenceStorageScreen, outputs, icon = DISPENSE_1) { init { dockRight = 3f tooltips.add(TranslatableComponent("otm.gui.experience.dispense", 1)) @@ -153,7 +153,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title set(value) {} } - object : LargeRectangleButtonPanel(this@EssenceStorageScreen, outputs, skinElement = DISPENSE_10) { + object : LargeRectangleButtonPanel(this@EssenceStorageScreen, outputs, icon = DISPENSE_10) { init { dockRight = 3f tooltips.add(TranslatableComponent("otm.gui.experience.dispense", 10)) @@ -168,7 +168,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title set(value) {} } - object : LargeRectangleButtonPanel(this@EssenceStorageScreen, outputs, skinElement = DISPENSE_ALL) { + object : LargeRectangleButtonPanel(this@EssenceStorageScreen, outputs, icon = DISPENSE_ALL) { init { dockRight = 3f tooltips.add(TranslatableComponent("otm.gui.experience.dispense_all")) @@ -186,7 +186,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title val customBar = HorizontalStripPanel(this, frame, height = 18f) customBar.dock = Dock.TOP - object : LargeRectangleButtonPanel(this@EssenceStorageScreen, customBar, skinElement = STORE_CUSTOM) { + object : LargeRectangleButtonPanel(this@EssenceStorageScreen, customBar, icon = STORE_CUSTOM) { init { tooltips.add(TranslatableComponent("otm.gui.experience.store", customDispense)) } @@ -227,7 +227,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title } } - object : LargeRectangleButtonPanel(this@EssenceStorageScreen, customBar, skinElement = DISPENSE_CUSTOM) { + object : LargeRectangleButtonPanel(this@EssenceStorageScreen, customBar, icon = DISPENSE_CUSTOM) { init { tooltips.add(TranslatableComponent("otm.gui.experience.dispense", customDispense)) } @@ -241,7 +241,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title set(value) {} } - object : LargeRectangleButtonPanel(this@EssenceStorageScreen, customBar, skinElement = SET_EXACT) { + object : LargeRectangleButtonPanel(this@EssenceStorageScreen, customBar, icon = SET_EXACT) { init { tooltips.add(TranslatableComponent("otm.gui.experience.set_exact", customDispense)) dock = Dock.RIGHT diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/CombinedContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/CombinedContainer.kt index 39f3ae231..36e2aa9d8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/CombinedContainer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/CombinedContainer.kt @@ -4,17 +4,18 @@ import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableSet import it.unimi.dsi.fastutil.ints.IntAVLTreeSet +import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.ints.IntSet import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap import net.minecraft.world.Container import net.minecraft.world.entity.player.Player +import net.minecraft.world.inventory.Slot +import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack -import ru.dbotthepony.mc.otm.container.util.ContainerSlot import ru.dbotthepony.mc.otm.container.util.IContainerSlot -import ru.dbotthepony.mc.otm.container.util.IIterableContainer +import ru.dbotthepony.mc.otm.container.util.containerSlot import ru.dbotthepony.mc.otm.container.util.iterator -import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.collect.concatIterators import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.flatMap @@ -24,21 +25,34 @@ import ru.dbotthepony.mc.otm.core.stream import java.util.LinkedList import java.util.stream.Stream -class CombinedContainer(containers: Stream>>) : Container, IIterableContainer { - constructor(vararg containers: Container) : this(containers.stream().map { it to (0 until it.containerSize).iterator() }) - constructor(containers: Collection) : this(containers.stream().map { it to (0 until it.containerSize).iterator() }) +class CombinedContainer(containers: Stream>>) : IMatteryContainer { + constructor(vararg containers: Container) : this(containers.stream().map { it to (0 until it.containerSize) }) + constructor(containers: Collection) : this(containers.stream().map { it to (0 until it.containerSize) }) - private val slots: List - private val slotsMap: Map> + private inner class Slot(override val slot: Int, val outer: IContainerSlot) : IContainerSlot by outer { + override val container: Container + get() = this@CombinedContainer + + override fun component1(): Int { + return super.component1() + } + + override fun component2(): ItemStack { + return super.component2() + } + } + + private val slots: List + private val slotsMap: Map> private val containers: Set private val fullCoverage: List - private val notFullCoverage: Map> + private val notFullCoverage: Map> init { - val list = ImmutableList.Builder() + val list = ImmutableList.Builder() var i = 0 val validationMap = Reference2ObjectOpenHashMap() - val slotsMap = Reference2ObjectOpenHashMap>() + val slotsMap = Reference2ObjectOpenHashMap>() for ((container, slots) in containers) { val validator = validationMap.computeIfAbsent(container, Object2ObjectFunction { IntAVLTreeSet() }) @@ -46,9 +60,8 @@ class CombinedContainer(containers: Stream>>) : Co for (slot in slots) { if (validator.add(slot)) { - i++ - val slotObj = ContainerSlot(slot, container) - list.add(slotObj) + val slotObj = container.containerSlot(slot) + list.add(Slot(i++, slotObj)) slotList.add(slotObj) } else { throw IllegalArgumentException("Duplicate mapping for $container at $i for slot $slot") @@ -107,27 +120,23 @@ class CombinedContainer(containers: Stream>>) : Co return true } - fun slotAt(index: Int): ContainerSlot { - return slots[index] - } - - override fun getItem(index: Int): ItemStack { + override fun getItem(slot: Int): ItemStack { // do not violate contract of getItem not throwing exceptions when index is invalid - return slots.getOrNull(index)?.item ?: ItemStack.EMPTY + return slots.getOrNull(slot)?.item ?: ItemStack.EMPTY } - override fun removeItem(index: Int, count: Int): ItemStack { - val data = slots.getOrNull(index) ?: return ItemStack.EMPTY - return data.container.removeItem(data.slot, count) + override fun removeItem(slot: Int, amount: Int): ItemStack { + val data = slots.getOrNull(slot) ?: return ItemStack.EMPTY + return data.outer.container.removeItem(data.outer.slot, amount) } - override fun removeItemNoUpdate(index: Int): ItemStack { - val data = slots.getOrNull(index) ?: return ItemStack.EMPTY - return data.container.removeItemNoUpdate(data.slot) + override fun removeItemNoUpdate(slot: Int): ItemStack { + val data = slots.getOrNull(slot) ?: return ItemStack.EMPTY + return data.outer.container.removeItemNoUpdate(data.outer.slot) } - override fun setItem(index: Int, value: ItemStack) { - slots.getOrNull(index)?.item = value + override fun setItem(slot: Int, itemStack: ItemStack) { + slots.getOrNull(slot)?.item = itemStack } override fun setChanged() { @@ -136,16 +145,6 @@ class CombinedContainer(containers: Stream>>) : Co } } - fun setChanged(index: Int) { - val data = slots.getOrNull(index) ?: return - - if (data.container is MatteryContainer) { - data.container.setChanged(data.slot) - } else { - data.container.setChanged() - } - } - override fun stillValid(player: Player): Boolean { for (container in containers) if (!container.stillValid(player)) @@ -168,33 +167,64 @@ class CombinedContainer(containers: Stream>>) : Co return slots.iterator() } + override fun containerSlot(slot: Int): IContainerSlot { + return slots[slot] + } + + override fun getSlotFilter(slot: Int): Item? { + return slots[slot].getFilter() + } + + override fun setChanged(slot: Int) { + slots[slot].setChanged() + } + + override fun setSlotFilter(slot: Int, filter: Item?): Boolean { + return slots[slot].setFilter(filter) + } + class Builder { - private var built = false - private val values = LinkedList>>() + private val values = ArrayList>>() fun add(container: Container): Builder { - check(!built) { "Already built!" } - values.add(container to (0 until container.containerSize).iterator()) + values.add(container to container.slotRange) return this } fun add(container: Container, slots: Iterator): Builder { - check(!built) { "Already built!" } - values.add(container to slots) + values.add(container to IntArrayList(slots)) + return this + } + + fun add(container: Container, slot: Int): Builder { + values.add(container to intArrayOf(slot).asIterable()) + return this + } + + fun add(container: Container, from: Int, to: Int): Builder { + values.add(container to (from .. to)) return this } fun add(container: Container, slots: Iterable): Builder { - check(!built) { "Already built!" } - values.add(container to slots.iterator()) + values.add(container to slots) return this } fun build(): CombinedContainer { - check(!built) { "Already built!" } - val value = CombinedContainer(values.stream()) - values.clear() - return value + return CombinedContainer(values.stream()) + } + } + + companion object { + fun fromMenuSlots(slots: Iterator): CombinedContainer { + val builder = Builder() + + for (slot in slots) { + builder.add(slot.container, slot.slotIndex) + } + + return builder.build() } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHandler.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHandler.kt index 25fe04cda..eefa76b79 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHandler.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHandler.kt @@ -3,8 +3,8 @@ package ru.dbotthepony.mc.otm.container import net.minecraft.world.item.ItemStack import net.minecraftforge.items.IItemHandler -class ContainerHandler @JvmOverloads internal constructor( - private val container: MatteryContainer, +class ContainerHandler( + private val container: IMatteryContainer, private val filter: HandlerFilter = HandlerFilter.Both, ) : IItemHandler { override fun getSlots() = container.containerSize diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHelpers.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHelpers.kt index 14d286ec9..9a2a0e79e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHelpers.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/ContainerHelpers.kt @@ -3,6 +3,8 @@ package ru.dbotthepony.mc.otm.container import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.ints.IntArraySet +import it.unimi.dsi.fastutil.ints.IntIterable +import it.unimi.dsi.fastutil.ints.IntIterator import it.unimi.dsi.fastutil.ints.IntSet import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap @@ -11,10 +13,14 @@ import net.minecraft.world.inventory.CraftingContainer import net.minecraft.world.item.ItemStack import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse import net.minecraftforge.fluids.capability.IFluidHandler +import ru.dbotthepony.mc.otm.container.util.IContainerSlot import ru.dbotthepony.mc.otm.container.util.ItemStackHashStrategy import ru.dbotthepony.mc.otm.container.util.slotIterator import ru.dbotthepony.mc.otm.core.addAll +import ru.dbotthepony.mc.otm.core.collect.filter +import ru.dbotthepony.mc.otm.core.collect.toList import ru.dbotthepony.mc.otm.core.isNotEmpty +import ru.dbotthepony.mc.otm.core.map import ru.dbotthepony.mc.otm.core.util.ItemStackSorter import kotlin.math.roundToInt @@ -27,13 +33,30 @@ inline operator fun Container.get(index: Int): ItemStack = getItem(index) @Suppress("nothing_to_inline") inline operator fun IFluidHandler.get(index: Int) = getFluidInTank(index) -fun Container.addItem(stack: ItemStack, range: IntRange, simulate: Boolean = false): ItemStack { - if (this is MatteryContainer) { - return this.addItem(stack, range, simulate) - } +val Container.slotRange: IntIterable get() { + return IntIterable { + val i = (0 until containerSize).iterator() - if (range.last >= containerSize || range.first < 0) - throw IllegalArgumentException("Invalid range: $range") + object : IntIterator { + override fun hasNext(): Boolean { + return i.hasNext() + } + + override fun remove() { + throw UnsupportedOperationException() + } + + override fun nextInt(): Int { + return i.nextInt() + } + } + } +} + +fun Container.addItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange): ItemStack { + if (this is IMatteryContainer) { + return this.addItem(stack, simulate, slots) + } if (stack.isEmpty) return stack @@ -41,7 +64,11 @@ fun Container.addItem(stack: ItemStack, range: IntRange, simulate: Boolean = fal val copy = stack.copy() // двигаем в одинаковые слоты - for (slot in range) { + var i = slots.intIterator() + + while (i.hasNext()) { + val slot = i.nextInt() + if (ItemStack.isSameItemSameTags(this[slot], copy)) { val slotStack = this[slot] val slotLimit = maxStackSize.coerceAtMost(slotStack.maxStackSize) @@ -65,7 +92,11 @@ fun Container.addItem(stack: ItemStack, range: IntRange, simulate: Boolean = fal } // двигаем в пустые слоты - for (slot in range) { + i = slots.intIterator() + + while (i.hasNext()) { + val slot = i.nextInt() + if (this[slot].isEmpty) { val diff = copy.count.coerceAtMost(maxStackSize.coerceAtMost(copy.maxStackSize)) @@ -87,8 +118,6 @@ fun Container.addItem(stack: ItemStack, range: IntRange, simulate: Boolean = fal return copy } -fun Container.addItem(stack: ItemStack, simulate: Boolean): ItemStack = addItem(stack, 0 until containerSize, simulate) - fun Container.vanishCursedItems() { for (slot in slotIterator()) { if (hasVanishingCurse(slot.item)) { @@ -258,3 +287,31 @@ operator fun CraftingContainer.get(column: Int, row: Int, flop: Boolean): ItemSt else get(column, row) } + +private object FilteredFirst : Comparator { + override fun compare(o1: IContainerSlot, o2: IContainerSlot): Int { + if (o1.hasFilter && o2.hasFilter) + return 0 + else if (o2.hasFilter) + return -1 + else if (o1.hasFilter) + return 1 + else + return 0 + } +} + +fun Container.sort(comparator: Comparator = ItemStackSorter.DEFAULT) { + if (isEmpty) + return + + val slots = slotIterator().filter { !it.isForbiddenForAutomation && it.getMaxStackSize() >= it.item.count }.toList() + + if (slots.isEmpty()) + return + + slots.sortWith(FilteredFirst.thenComparing(comparator.map(IContainerSlot::item))) + val items = slots.map { it.item.copy() } + slots.forEach { it.remove() } + items.forEach { addItem(it, false) } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/DynamicallyProxiedContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/DynamicallyProxiedContainer.kt new file mode 100644 index 000000000..b2e400392 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/DynamicallyProxiedContainer.kt @@ -0,0 +1,81 @@ +package ru.dbotthepony.mc.otm.container + +import net.minecraft.world.Container +import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import java.util.function.Predicate +import java.util.function.Supplier + +/** + * because mods tend to do crazy shit + */ +class DynamicallyProxiedContainer(private val toProxy: Supplier) : IContainer { + override fun clearContent() { + return toProxy.get().clearContent() + } + + override fun getContainerSize(): Int { + return toProxy.get().containerSize + } + + override fun isEmpty(): Boolean { + return toProxy.get().isEmpty + } + + override fun getItem(slot: Int): ItemStack { + return toProxy.get().getItem(slot) + } + + override fun removeItem(slot: Int, amount: Int): ItemStack { + return toProxy.get().removeItem(slot, amount) + } + + override fun removeItemNoUpdate(slot: Int): ItemStack { + return toProxy.get().removeItemNoUpdate(slot) + } + + override fun setItem(slot: Int, itemStack: ItemStack) { + return toProxy.get().setItem(slot, itemStack) + } + + override fun setChanged() { + return toProxy.get().setChanged() + } + + override fun stillValid(player: Player): Boolean { + return toProxy.get().stillValid(player) + } + + override fun getMaxStackSize(): Int { + return toProxy.get().getMaxStackSize() + } + + override fun startOpen(player: Player) { + toProxy.get().startOpen(player) + } + + override fun stopOpen(player: Player) { + toProxy.get().stopOpen(player) + } + + override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean { + return toProxy.get().canPlaceItem(slot, itemStack) + } + + override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean { + return toProxy.get().canTakeItem(container, slot, itemStack) + } + + override fun countItem(item: Item): Int { + return toProxy.get().countItem(item) + } + + override fun hasAnyOf(items: Set): Boolean { + return toProxy.get().hasAnyOf(items) + } + + override fun hasAnyMatching(predicate: Predicate): Boolean { + return toProxy.get().hasAnyMatching(predicate) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/IContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/IContainer.kt index 71e7c696c..3afa4ab72 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/IContainer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/IContainer.kt @@ -7,6 +7,7 @@ import net.minecraft.world.item.ItemStack import java.util.function.Predicate // passthrough all default methods to fix Kotlin bug related to implementation delegation not properly working on Java interfaces +// and also to give params proper names // https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods interface IContainer : Container { override fun getMaxStackSize(): Int { @@ -41,6 +42,16 @@ interface IContainer : Container { return super.hasAnyMatching(predicate) } + override fun clearContent() + override fun getContainerSize(): Int + override fun isEmpty(): Boolean + override fun getItem(slot: Int): ItemStack + override fun removeItem(slot: Int, amount: Int): ItemStack + override fun removeItemNoUpdate(slot: Int): ItemStack + override fun setItem(slot: Int, itemStack: ItemStack) + override fun setChanged() + + override fun stillValid(player: Player): Boolean companion object { fun wrap(container: Container): IContainer { if (container is IContainer) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/IMatteryContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/IMatteryContainer.kt new file mode 100644 index 000000000..b1d30a73f --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/IMatteryContainer.kt @@ -0,0 +1,217 @@ +package ru.dbotthepony.mc.otm.container + +import it.unimi.dsi.fastutil.ints.IntIterable +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import ru.dbotthepony.mc.otm.container.util.IContainerSlot +import ru.dbotthepony.mc.otm.core.collect.filter +import ru.dbotthepony.mc.otm.core.collect.map +import ru.dbotthepony.mc.otm.core.isNotEmpty + +interface IMatteryContainer : IContainer, Iterable { + fun getSlotFilter(slot: Int): Item? + + /** + * @return whenever the filter was set. Returns false only if container can't be filtered. + */ + fun setSlotFilter(slot: Int, filter: Item? = null): Boolean { + return false + } + + fun setChanged(slot: Int) + + /** + * Iterates over non-empty itemstacks of this container + */ + override fun iterator(): Iterator { + return iterator(true) + } + + /** + * Iterates non-empty slots of this container + */ + fun slotIterator(): Iterator { + return slotIterator(true) + } + + fun iterator(nonEmpty: Boolean): Iterator { + if (nonEmpty) { + return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty } + } else { + return (0 until containerSize).iterator().map { this[it] } + } + } + + open class ContainerSlot(override val slot: Int, override val container: IMatteryContainer) : IContainerSlot { + override val isForbiddenForAutomation: Boolean + get() = container.isSlotForbiddenForAutomation(slot) + + override fun getFilter(): Item? { + return container.getSlotFilter(slot) + } + + override fun setFilter(filter: Item?): Boolean { + return container.setSlotFilter(slot, filter) + } + + override fun getMaxStackSize(item: ItemStack): Int { + return container.getMaxStackSize(slot, item) + } + + override fun setChanged() { + container.setChanged(slot) + } + } + + fun slotIterator(nonEmpty: Boolean): Iterator { + if (nonEmpty) { + return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { containerSlot(it) } + } else { + return (0 until containerSize).iterator().map { containerSlot(it) } + } + } + + fun containerSlot(slot: Int): IContainerSlot { + return ContainerSlot(slot, this) + } + + fun hasSlotFilter(slot: Int) = getSlotFilter(slot) !== null + fun isSlotForbiddenForAutomation(slot: Int) = getSlotFilter(slot) === Items.AIR + + fun testSlotFilter(slot: Int, itemStack: ItemStack): Boolean { + return testSlotFilter(slot, itemStack.item) + } + + fun testSlotFilter(slot: Int, item: Item): Boolean { + if (getSlotFilter(slot) == null) { + return true + } else { + return getSlotFilter(slot) === item + } + } + + fun getMaxStackSize(slot: Int, itemStack: ItemStack) = maxStackSize.coerceAtMost(itemStack.maxStackSize) + + private fun addItem(stack: ItemStack, simulate: Boolean, filterPass: Boolean, slots: IntIterable, onlyIntoExisting: Boolean, popTime: Int?, ignoreFilters: Boolean): ItemStack { + if (stack.isEmpty) + return stack + + // двигаем в одинаковые слоты + var i = slots.intIterator() + + while (i.hasNext()) { + val slot = i.nextInt() + + if ( + (ignoreFilters || !isSlotForbiddenForAutomation(slot)) && + ItemStack.isSameItemSameTags(getItem(slot), stack) && + (ignoreFilters || !filterPass && !hasSlotFilter(slot) || filterPass && hasSlotFilter(slot) && testSlotFilter(slot, stack)) + ) { + val slotStack = getItem(slot) + val slotLimit = getMaxStackSize(slot, slotStack) + + if (slotStack.count < slotLimit) { + val newCount = (slotStack.count + stack.count).coerceAtMost(slotLimit) + val diff = newCount - slotStack.count + + if (!simulate) { + slotStack.count = newCount + setChanged(slot) + + if (popTime != null) { + slotStack.popTime = popTime + } + } + + stack.shrink(diff) + + if (stack.isEmpty) { + return stack + } + } + } + } + + if (!onlyIntoExisting) { + i = slots.intIterator() + + // двигаем в пустые слоты + while (i.hasNext()) { + val slot = i.nextInt() + + if ( + getItem(slot).isEmpty && + (ignoreFilters || !isSlotForbiddenForAutomation(slot)) && + (ignoreFilters || !filterPass && !hasSlotFilter(slot) || filterPass && hasSlotFilter(slot) && testSlotFilter(slot, stack)) + ) { + val diff = stack.count.coerceAtMost(getMaxStackSize(slot, stack)) + + if (!simulate) { + val copyToPut = stack.copy() + copyToPut.count = diff + setItem(slot, copyToPut) + + if (popTime != null) { + copyToPut.popTime = popTime + } + } + + stack.shrink(diff) + + if (stack.isEmpty) { + return stack + } + } + } + } + + return stack + } + + fun addItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): ItemStack { + if (stack.isEmpty) + return stack + + if (ignoreFilters) { + return addItem(stack.copy(), simulate, true, slots, onlyIntoExisting, popTime, true) + } else { + var copy = addItem(stack.copy(), simulate, true, slots, onlyIntoExisting, popTime, false) + copy = addItem(copy, simulate, false, slots, onlyIntoExisting, popTime, false) + return copy + } + } + + fun handler(filter: HandlerFilter = HandlerFilter.Both): ContainerHandler { + return ContainerHandler(this, filter) + } + + /** + * Unlike [addItem], modifies original [stack] + * + * @return Whenever [stack] was modified + */ + fun consumeItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): Boolean { + if (stack.isEmpty) + return false + + val result = addItem(stack, simulate, slots, onlyIntoExisting, popTime, ignoreFilters) + + if (result.count != stack.count) { + if (!simulate) { + stack.count = result.count + } + + return true + } + + return false + } + + fun fullyAddItem(stack: ItemStack, slots: IntIterable = slotRange, ignoreFilters: Boolean = false): Boolean { + if (!addItem(stack, true, slots, ignoreFilters).isEmpty) + return false + + return addItem(stack, false, slots, ignoreFilters).isEmpty + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt index ea8e4f840..9061de489 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt @@ -11,7 +11,7 @@ import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag import net.minecraft.nbt.Tag import net.minecraft.resources.ResourceLocation -import kotlin.jvm.JvmOverloads +import net.minecraft.world.Container import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.StackedContents import net.minecraft.world.inventory.StackedContentsCompatible @@ -19,9 +19,7 @@ import net.minecraft.world.item.Item import net.minecraft.world.item.Items import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.registries.ForgeRegistries -import ru.dbotthepony.mc.otm.container.util.ContainerSlot import ru.dbotthepony.mc.otm.container.util.IContainerSlot -import ru.dbotthepony.mc.otm.container.util.IIterableContainer import ru.dbotthepony.mc.otm.core.addSorted import ru.dbotthepony.mc.otm.core.collect.any import ru.dbotthepony.mc.otm.core.collect.count @@ -44,10 +42,9 @@ import java.util.function.Supplier import java.util.stream.Stream import java.util.stream.StreamSupport import kotlin.collections.ArrayList -import kotlin.collections.Iterator @Suppress("UNUSED") -open class MatteryContainer(protected val watcher: Runnable, private val size: Int) : IContainer, IIterableContainer, Iterable, INBTSerializable, StackedContentsCompatible { +open class MatteryContainer(protected val watcher: Runnable, private val size: Int) : IMatteryContainer, INBTSerializable, StackedContentsCompatible { constructor(size: Int) : this({}, size) init { @@ -134,7 +131,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I return field } - fun setSlotFilter(slot: Int, filter: Item? = null) { + final override fun setSlotFilter(slot: Int, filter: Item?): Boolean { if (filters[slot] !== filter) { filters[slot] = filter @@ -146,17 +143,19 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I } } } + + return true } - fun getSlotFilter(slot: Int) = filters[slot] - fun hasSlotFilter(slot: Int) = filters[slot] !== null - fun isSlotForbiddenForAutomation(slot: Int) = filters[slot] === Items.AIR + final override fun getSlotFilter(slot: Int) = filters[slot] + final override fun hasSlotFilter(slot: Int) = filters[slot] !== null + final override fun isSlotForbiddenForAutomation(slot: Int) = filters[slot] === Items.AIR - fun testSlotFilter(slot: Int, itemStack: ItemStack): Boolean { + final override fun testSlotFilter(slot: Int, itemStack: ItemStack): Boolean { return testSlotFilter(slot, itemStack.item) } - fun testSlotFilter(slot: Int, item: Item): Boolean { + final override fun testSlotFilter(slot: Int, item: Item): Boolean { if (filters[slot] == null) { return true } else { @@ -283,137 +282,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I } } - fun hasEmptySlot(): Boolean { - for (i in 0 until size) { - if (this[i].isEmpty) { - return true - } - } - - return false - } - - fun handler(filter: HandlerFilter = HandlerFilter.Both): ContainerHandler { - return ContainerHandler(this, filter) - } - - open fun getMaxStackSize(slot: Int, itemStack: ItemStack) = maxStackSize.coerceAtMost(itemStack.maxStackSize) - - /** - * @return Leftover [ItemStack] - */ - @JvmOverloads - fun addItem(stack: ItemStack, range: IntRange, simulate: Boolean = false, onlyIntoExisting: Boolean = false, popTime: Int? = null): ItemStack { - if (range.last >= size || range.first < 0) - throw IllegalArgumentException("Invalid range: $range") - - if (stack.isEmpty) - return stack - - val copy = stack.copy() - - // двигаем в одинаковые слоты - for (slot in range) { - if (ItemStack.isSameItemSameTags(slots[slot], copy)) { - val slotStack = slots[slot] - val slotLimit = getMaxStackSize(slot, slotStack) - - if (slotStack.count < slotLimit) { - val newCount = (slotStack.count + copy.count).coerceAtMost(slotLimit) - val diff = newCount - slotStack.count - - if (!simulate) { - val old = slotStack.copy() - slotStack.count = newCount - trackedSlots[slot] = slotStack.copy() - changeset++ - internalSetChanged(slot, slotStack, old) - - if (popTime != null) { - slotStack.popTime = popTime - } - } - - copy.shrink(diff) - - if (copy.isEmpty) { - return copy - } - } - } - } - - if (!onlyIntoExisting) { - // двигаем в пустые слоты - for (slot in range) { - if (slots[slot].isEmpty) { - val diff = copy.count.coerceAtMost(getMaxStackSize(slot, copy)) - - if (!simulate) { - val copyToPut = copy.copy() - copyToPut.count = diff - slots[slot] = copyToPut - trackedSlots[slot] = copyToPut.copy() - updateEmptyFlag(slot) - changeset++ - internalSetChanged(slot, copyToPut, ItemStack.EMPTY) - - if (popTime != null) { - copyToPut.popTime = popTime - } - } - - copy.shrink(diff) - - if (copy.isEmpty) { - return copy - } - } - } - } - - return copy - } - - fun addItem(stack: ItemStack, simulate: Boolean, onlyIntoExisting: Boolean = false, popTime: Int? = null): ItemStack { - return addItem(stack, 0 until size, simulate, onlyIntoExisting = onlyIntoExisting, popTime = popTime) - } - - /** - * Unlike [addItem], modifies original [stack] - * - * @return Whenever [stack] was modified - */ - fun consumeItem(stack: ItemStack, simulate: Boolean, onlyIntoExisting: Boolean = false, popTime: Int? = null): Boolean { - if (stack.isEmpty) - return false - - val result = addItem(stack, 0 until size, simulate, onlyIntoExisting = onlyIntoExisting, popTime = popTime) - - if (result.count != stack.count) { - if (!simulate) { - stack.count = result.count - } - - return true - } - - return false - } - - @JvmOverloads - fun fullyAddItem(stack: ItemStack, start: Int = 0, end: Int = size - 1): Boolean { - return fullyAddItem(stack, start .. end) - } - - fun fullyAddItem(stack: ItemStack, range: IntRange): Boolean { - if (!addItem(stack, range, true).isEmpty) - return false - - return addItem(stack, range, false).isEmpty - } - - override fun isEmpty(): Boolean { + final override fun isEmpty(): Boolean { return nonEmptyIndices.isEmpty } @@ -484,17 +353,17 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I return old } - final override fun setItem(slot: Int, stack: ItemStack) { - if (slots[slot].isEmpty && stack.isEmpty || stack === slots[slot]) + final override fun setItem(slot: Int, itemStack: ItemStack) { + if (slots[slot].isEmpty && itemStack.isEmpty || itemStack === slots[slot]) return val old = slots[slot] - slots[slot] = if (stack.isEmpty) ItemStack.EMPTY else stack - trackedSlots[slot] = if (stack.isEmpty) ItemStack.EMPTY else stack.copy() + slots[slot] = if (itemStack.isEmpty) ItemStack.EMPTY else itemStack + trackedSlots[slot] = if (itemStack.isEmpty) ItemStack.EMPTY else itemStack.copy() updateEmptyFlag(slot) changeset++ - internalSetChanged(slot, stack, old) + internalSetChanged(slot, itemStack, old) } final override fun setChanged() { @@ -512,7 +381,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I } } - fun setChanged(slot: Int) { + final override fun setChanged(slot: Int) { if (!slots[slot].equals(trackedSlots[slot], false)) { trackedSlots[slot] = slots[slot].copy() updateEmptyFlag(slot) @@ -613,22 +482,50 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I } } + private inner class Slot(override val slot: Int) : IContainerSlot { + override val container: Container + get() = this@MatteryContainer + + override val isForbiddenForAutomation: Boolean + get() = isSlotForbiddenForAutomation(slot) + + override fun getFilter(): Item? { + return getSlotFilter(slot) + } + + override fun setFilter(filter: Item?): Boolean { + return setSlotFilter(slot, filter) + } + + override fun getMaxStackSize(item: ItemStack): Int { + return getMaxStackSize(slot, item) + } + + override fun setChanged() { + setChanged(slot) + } + } + final override fun slotIterator(): kotlin.collections.Iterator { indicesReferenced = true - return nonEmptyIndices.iterator().map { ContainerSlot(it, this) } + return nonEmptyIndices.iterator().map { Slot(it) } } final override fun slotIterator(nonEmpty: Boolean): kotlin.collections.Iterator { if (!nonEmpty) { - return (0 until size).iterator().map { ContainerSlot(it, this) } + return (0 until size).iterator().map { Slot(it) } } else if (isEmpty) { return emptyIterator() } else { indicesReferenced = true - return nonEmptyIndices.iterator().map { ContainerSlot(it, this) } + return nonEmptyIndices.iterator().map { Slot(it) } } } + final override fun containerSlot(slot: Int): IContainerSlot { + return Slot(slot) + } + final override fun countItem(item: Item): Int { return iterator().filter { it.item == item }.count().toInt() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/util/Iterators.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/util/Iterators.kt index 94bdfeaba..a0063258e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/util/Iterators.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/util/Iterators.kt @@ -3,6 +3,8 @@ package ru.dbotthepony.mc.otm.container.util import net.minecraft.world.Container import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import ru.dbotthepony.mc.otm.container.IMatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.get import ru.dbotthepony.mc.otm.container.set @@ -11,6 +13,10 @@ import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.isNotEmpty +/** + * While this somewhat similar to [net.minecraft.world.inventory.Slot], this slot is not meant + * for Player interaction. + */ interface IContainerSlot : GetterSetter { val slot: Int val container: Container @@ -18,28 +24,23 @@ interface IContainerSlot : GetterSetter { operator fun component1() = slot operator fun component2() = item + fun getMaxStackSize(item: ItemStack = this.item): Int { + return container.maxStackSize + } + val isForbiddenForAutomation: Boolean get() { - val container = container - - if (container is MatteryContainer) { - return container.isSlotForbiddenForAutomation(slot) - } else { - return false - } + return getFilter() === Items.AIR } - val filter: Item? get() { - val container = container + fun getFilter(): Item? - if (container is MatteryContainer) { - return container.getSlotFilter(slot) - } else { - return null - } - } + /** + * @return whenever the filter was set. Returns false only if container can't be filtered. + */ + fun setFilter(filter: Item? = null): Boolean val hasFilter: Boolean - get() = filter != null + get() = getFilter() != null fun remove() { container[slot] = ItemStack.EMPTY @@ -54,13 +55,7 @@ interface IContainerSlot : GetterSetter { } fun setChanged() { - val container = container - - if (container is MatteryContainer) { - container.setChanged(slot) - } else { - container.setChanged() - } + container.setChanged() } var item: ItemStack @@ -78,32 +73,28 @@ class ContainerSlot(override val slot: Int, override val container: Container) : init { require(slot in 0 until container.containerSize) { "Slot out of bounds: $slot (container: $container with size ${container.containerSize})" } } + + override fun getFilter(): Item? { + return null + } + + override fun setFilter(filter: Item?): Boolean { + return false + } } -interface IIterableContainer : Iterable { - /** - * Iterates over non-empty itemstacks of this container - */ - override fun iterator(): Iterator { - return iterator(true) +fun Container.containerSlot(slot: Int): IContainerSlot { + if (this is IMatteryContainer) { + return containerSlot(slot) + } else { + return ContainerSlot(slot, this) } - - fun iterator(nonEmpty: Boolean): Iterator - - /** - * Iterates non-empty slots of this container - */ - fun slotIterator(): Iterator { - return slotIterator(true) - } - - fun slotIterator(nonEmpty: Boolean): Iterator } operator fun Container.iterator() = iterator(true) fun Container.iterator(nonEmpty: Boolean): Iterator { - if (this is IIterableContainer) { + if (this is IMatteryContainer) { return iterator(nonEmpty) } else if (nonEmpty) { return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty } @@ -113,7 +104,7 @@ fun Container.iterator(nonEmpty: Boolean): Iterator { } fun Container.slotIterator(nonEmpty: Boolean = true): Iterator { - if (this is IIterableContainer) { + if (this is IMatteryContainer) { return slotIterator(nonEmpty) } else if (nonEmpty) { return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { ContainerSlot(it, this) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 5e7847a5c..41d38d12a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -362,17 +362,12 @@ fun Stream.asIterable(): Iterable { } } -// Kotlin type safety: -// since Java generics are invariant, -// and can not distinguish between null and non-null type parameters -// we need to tell compiler that Comparator.nullsFirst actually has next signature: -// fun Comparator.nullsFirst(original: Comparator): Comparator -fun Comparator.nullsFirst(): Comparator { - return Comparator.nullsFirst(this as Comparator) +fun Comparator.nullsFirst(): Comparator { + return Comparator.nullsFirst(this) } -fun Comparator.nullsLast(): Comparator { - return Comparator.nullsLast(this as Comparator) +fun Comparator.nullsLast(): Comparator { + return Comparator.nullsLast(this) } class MappedComparator(private val parent: Comparator, private val mapper: (T) -> O) : Comparator { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/Iterables.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/Iterables.kt new file mode 100644 index 000000000..469b95915 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/Iterables.kt @@ -0,0 +1,24 @@ +package ru.dbotthepony.mc.otm.core.collect + +import it.unimi.dsi.fastutil.ints.IntIterable +import it.unimi.dsi.fastutil.ints.IntIterator + +fun IntRange.asIterable(): IntIterable { + return IntIterable { + val i = this@asIterable.iterator() + + object : IntIterator { + override fun hasNext(): Boolean { + return i.hasNext() + } + + override fun remove() { + throw UnsupportedOperationException() + } + + override fun nextInt(): Int { + return i.nextInt() + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterators.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterators.kt index 42e4476f5..5377de346 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterators.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/collect/StreamyIterators.kt @@ -294,8 +294,8 @@ fun Iterator.collect(collector: Collector): R { return collector.finisher().apply(instance) } -fun Iterator.toList(): MutableList { - val result = ArrayList() +fun Iterator.toList(expectedSize: Int = 16): MutableList { + val result = ArrayList(expectedSize) result.addAll(this) return result } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/ItemSorter.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/ItemSorter.kt index 3cf948b94..6319572f0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/ItemSorter.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/ItemSorter.kt @@ -19,12 +19,12 @@ import ru.dbotthepony.mc.otm.matter.MatterManager import ru.dbotthepony.mc.otm.storage.ItemStorageStack import ru.dbotthepony.mc.otm.client.render.Widgets18 -private fun Comparator.stacks(): Comparator { - return Comparator { o1, o2 -> this@stacks.compare(o1.item, o2.item) }.nullsFirst() +private fun Comparator.stacks(): Comparator { + return Comparator { o1, o2 -> this@stacks.compare(o1.item, o2.item) } } -private fun Comparator.storage(): Comparator { - return Comparator { o1, o2 -> this@storage.compare(o1.item, o2.item) }.nullsFirst() +private fun Comparator.storage(): Comparator { + return Comparator { o1, o2 -> this@storage.compare(o1.item, o2.item) } } object CreativeMenuItemComparator : Comparator { @@ -156,7 +156,7 @@ object ItemStorageStackCountComparator : Comparator { val NullsLast = nullsLast() } -enum class ItemSorter(comparator: Comparator, private val sTitle: Component, icon: Lazy) : Comparator by comparator.nullsFirst() { +enum class ItemSorter(comparator: Comparator, private val sTitle: Component, icon: Lazy) : Comparator by comparator { DEFAULT(CreativeMenuItemComparator, TranslatableComponent("otm.gui.sorting.default"), lazy { Widgets18.SORT_DEFAULT }), NAME(ItemLocalizedNameComparator.thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.name"), lazy { Widgets18.SORT_ALPHABET }), ID(ItemIDComparator.thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.id"), lazy { Widgets18.SORT_ID }), @@ -167,14 +167,12 @@ enum class ItemSorter(comparator: Comparator, private val sTitle: Componen val icon: IGUIRenderable by icon val title: MutableComponent get() = sTitle.copy() - val suppliers = suppliers() - val reversed: Comparator = reversed() } -enum class ItemStackSorter(comparator: Comparator, private val sTitle: Component, icon: Lazy) : Comparator by comparator { +enum class ItemStackSorter(comparator: Comparator, private val sTitle: Component, icon: Lazy) : Comparator by comparator { DEFAULT(ItemSorter.DEFAULT.stacks(), ItemSorter.DEFAULT.title, lazy { Widgets18.SORT_DEFAULT }), - COUNT(ItemStackCountComparator.NullsFirst.thenComparing(ItemSorter.DEFAULT.stacks()), TranslatableComponent("otm.gui.sorting.count"), lazy { Widgets18.SORT_COUNT }), - NAME(ItemStackNameComparator.NullsFirst.thenComparing(ItemSorter.DEFAULT.stacks()), ItemSorter.NAME.title, lazy { Widgets18.SORT_ALPHABET }), + COUNT(ItemStackCountComparator.thenComparing(ItemSorter.DEFAULT.stacks()), TranslatableComponent("otm.gui.sorting.count"), lazy { Widgets18.SORT_COUNT }), + NAME(ItemStackNameComparator.thenComparing(ItemSorter.DEFAULT.stacks()), ItemSorter.NAME.title, lazy { Widgets18.SORT_ALPHABET }), ID(ItemSorter.ID.stacks(), ItemSorter.ID.title, lazy { Widgets18.SORT_ID }), MOD(ItemSorter.MOD.stacks(), ItemSorter.MOD.title, lazy { Widgets18.SORT_MODID }), MATTER_VALUE(ItemSorter.MATTER_VALUE.stacks(), ItemSorter.MATTER_VALUE.title, lazy { Widgets18.SORT_MATTER_VALUE }), @@ -182,14 +180,12 @@ enum class ItemStackSorter(comparator: Comparator, private val sTitl val icon: IGUIRenderable by icon val title: MutableComponent get() = sTitle.copy() - val suppliers = suppliers() - val reversed: Comparator = reversed() } -enum class ItemStorageStackSorter(comparator: Comparator, private val sTitle: Component, icon: Lazy) : Comparator by comparator { +enum class ItemStorageStackSorter(comparator: Comparator, private val sTitle: Component, icon: Lazy) : Comparator by comparator { DEFAULT(ItemSorter.DEFAULT.storage(), ItemSorter.DEFAULT.title, lazy { Widgets18.SORT_DEFAULT }), - COUNT(ItemStorageStackCountComparator.NullsFirst.thenComparing(ItemSorter.DEFAULT.storage()), TranslatableComponent("otm.gui.sorting.count"), lazy { Widgets18.SORT_COUNT }), - NAME(ItemStorageStackNameComparator.NullsFirst.thenComparing(ItemSorter.DEFAULT.storage()), ItemSorter.NAME.title, lazy { Widgets18.SORT_ALPHABET }), + COUNT(ItemStorageStackCountComparator.thenComparing(ItemSorter.DEFAULT.storage()), TranslatableComponent("otm.gui.sorting.count"), lazy { Widgets18.SORT_COUNT }), + NAME(ItemStorageStackNameComparator.thenComparing(ItemSorter.DEFAULT.storage()), ItemSorter.NAME.title, lazy { Widgets18.SORT_ALPHABET }), ID(ItemSorter.ID.storage(), ItemSorter.ID.title, lazy { Widgets18.SORT_ID }), MOD(ItemSorter.MOD.storage(), ItemSorter.MOD.title, lazy { Widgets18.SORT_MODID }), MATTER_VALUE(ItemSorter.MATTER_VALUE.storage(), ItemSorter.MATTER_VALUE.title, lazy { Widgets18.SORT_MATTER_VALUE }), @@ -197,6 +193,4 @@ enum class ItemStorageStackSorter(comparator: Comparator, pri val icon: IGUIRenderable by icon val title: MutableComponent get() = sTitle.copy() - val suppliers = suppliers() - val reversed: Comparator = reversed() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/QuantumBatteryItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/QuantumBatteryItem.kt index 7a37f586e..d1c408685 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/QuantumBatteryItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/QuantumBatteryItem.kt @@ -24,7 +24,7 @@ import net.minecraftforge.network.NetworkEvent import net.minecraftforge.registries.ForgeRegistries import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.MatteryCapability -import ru.dbotthepony.mc.otm.capability.allItems +import ru.dbotthepony.mc.otm.capability.trackedItems import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.getBarColor import ru.dbotthepony.mc.otm.capability.energy.getBarWidth @@ -347,7 +347,7 @@ class QuantumBatteryItem(val savedataID: String, val balanceValues: EnergyBalanc for (ply in event.server.playerList.players) { val networkedChannels = ObjectOpenHashSet(0) - for (item in ply.allItems().filter { it.isNotEmpty && it.item is QuantumBatteryItem }) { + for (item in ply.trackedItems().filter { it.isNotEmpty && it.item is QuantumBatteryItem }) { val power = item.getCapability(MatteryCapability.ENERGY).orThrow() as Power power.updateValues() if (power.values.isServer && networkedChannels.add(power.values.uuid)) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt index fca2c7320..0ad8e4d61 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt @@ -33,9 +33,10 @@ import ru.dbotthepony.mc.otm.client.minecraft 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.IMatteryContainer import ru.dbotthepony.mc.otm.container.ItemFilter -import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.UpgradeContainer +import ru.dbotthepony.mc.otm.container.sort import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet import ru.dbotthepony.mc.otm.core.collect.ConditionalSet @@ -105,8 +106,6 @@ abstract class MatteryMenu( private val _playerInventorySlots = ArrayList() private val _playerHotbarSlots = ArrayList() - private val _playerMainSlots = ArrayList() - private val _playerExoSuitSlots = ArrayList() private val _playerCombinedInventorySlots = ArrayList() private val _exopackChargeSlots = ArrayList() @@ -189,7 +188,7 @@ abstract class MatteryMenu( fun intInput(allowSpectators: Boolean = false, handler: (Int) -> Unit) = PlayerInput(VarIntValueCodec, allowSpectators, handler) /** - * hotbar + inventory + exosuit (in this order) + * hotbar + inventory + Exopack (in this order) */ val playerInventorySlots: List = Collections.unmodifiableList(_playerInventorySlots) @@ -199,17 +198,7 @@ abstract class MatteryMenu( val playerHotbarSlots: List = Collections.unmodifiableList(_playerHotbarSlots) /** - * inventory only - */ - val playerMainSlots: List = Collections.unmodifiableList(_playerMainSlots) - - /** - * exosuit only - */ - val playerExoSuitSlots: List = Collections.unmodifiableList(_playerExoSuitSlots) - - /** - * inventory + exosuit (in this order) + * inventory + Exopack (in this order) */ val playerCombinedInventorySlots: List = Collections.unmodifiableList(_playerCombinedInventorySlots) @@ -217,6 +206,9 @@ abstract class MatteryMenu( val exopackPowerLevel = ProfiledLevelGaugeWidget>(mSynchronizer) + var sortInventoryInput: PlayerInput? = null + private set + var offhandSlot: InventorySlot? = null protected set @@ -267,7 +259,7 @@ abstract class MatteryMenu( return addFilterSlots(slots) } - open inner class InventorySlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : UserFilteredSlot(container, index, x, y) { + open inner class InventorySlot(container: Container, index: Int, addFilter: Boolean = false) : UserFilteredSlot(container, index, 0, 0) { override fun mayPlace(itemStack: ItemStack): Boolean { return !isInventorySlotLocked(index) && super.mayPlace(itemStack) } @@ -276,61 +268,30 @@ abstract class MatteryMenu( return !isInventorySlotLocked(index) && super.mayPickup(player) } - override fun isSameInventory(other: Slot): Boolean { - if (container === inventory || container === player.matteryPlayer?.exopackContainer) - return (other.container === inventory || other.container === player.matteryPlayer?.exopackContainer) && isSameFilter(other) - - return super.isSameInventory(other) - } - var chargeFlag: GetterSetter? = null private set init { - val mattery = player.matteryPlayer + val mattery = player.matteryPlayer!! - if (mattery != null) { - if (container === inventory) { - if (slotIndex in mattery.regularSlotFilters.indices) { - filter = GetterSetter.of( - getter = { mattery.regularSlotFilters[slotIndex].value }, - setter = nullableItemInput(true) { mattery.regularSlotFilters[slotIndex].value = it }::accept - ) - } + if (addFilter) { + val mContainer = container as IMatteryContainer - if (slotIndex in mattery.regularSlotChargeFlag.indices) { - val input = booleanInput(true) { mattery.regularSlotChargeFlag[slotIndex].boolean = it } - - chargeFlag = GetterSetter.of( - getter = { mattery.regularSlotChargeFlag[slotIndex].boolean }, - setter = input::accept - ) - } - } else if (container === mattery.exopackContainer) { - filter = GetterSetter.of( - getter = { mattery.exopackContainer.getSlotFilter(slotIndex) }, - setter = nullableItemInput(true) { mattery.exopackContainer.setSlotFilter(slotIndex, it) }::accept - ) - - val input = booleanInput(true) { if (it) mattery.exoPackSlotsChargeFlag.add(slotIndex) else mattery.exoPackSlotsChargeFlag.remove(slotIndex) } - - chargeFlag = GetterSetter.of( - getter = { slotIndex in mattery.exoPackSlotsChargeFlag }, - setter = input::accept - ) - } else { - filter = null - } - } else { - filter = null + filter = GetterSetter.of( + getter = { mContainer.getSlotFilter(slotIndex) }, + setter = nullableItemInput(true) { mContainer.setSlotFilter(slotIndex, it) }::accept + ) } + + chargeFlag = GetterSetter.of( + getter = { slotIndex in mattery.slotsChargeFlag }, + setter = booleanInput(true) { if (it) mattery.slotsChargeFlag.add(slotIndex) else mattery.slotsChargeFlag.remove(slotIndex) }::accept + ) } } - open inner class EquipmentSlot(container: Container, index: Int, val type: net.minecraft.world.entity.EquipmentSlot, x: Int = 0, y: Int = 0) : InventorySlot(container, index, x, y) { - constructor(type: net.minecraft.world.entity.EquipmentSlot, x: Int = 0, y: Int = 0) : this( - inventory, 34 + type.ordinal, type, x, y - ) + open inner class EquipmentSlot(container: Container, index: Int, val type: net.minecraft.world.entity.EquipmentSlot) : InventorySlot(container, index) { + constructor(type: net.minecraft.world.entity.EquipmentSlot) : this(inventory, 34 + type.ordinal, type) override fun mayPlace(itemStack: ItemStack): Boolean { return super.mayPlace(itemStack) && itemStack.canEquip(type, inventory.player) @@ -351,6 +312,8 @@ abstract class MatteryMenu( protected fun addInventorySlots(autoFrame: Boolean = !player.isSpectator) { check(_playerInventorySlots.isEmpty()) { "Already created inventory slots" } + val mattery = player.matteryPlayer ?: return + autoCreateInventoryFrame = autoFrame offhandSlot = object : InventorySlot(inventory, 40) { @@ -362,11 +325,12 @@ abstract class MatteryMenu( mapQuickMoveToInventory(offhandSlot!!) addSlot(offhandSlot!!) - for (i in 0 .. 35) { - val slot = InventorySlot(inventory, i) + for (i in 0 until mattery.combinedInventory.containerSize) { + if (i in Inventory.INVENTORY_SIZE .. player.inventory.containerSize) continue + + val slot = InventorySlot(mattery.combinedInventory, i, true) _playerInventorySlots.add(slot) - _playerMainSlots.add(slot) if (i <= 8) _playerHotbarSlots.add(slot) @@ -378,21 +342,7 @@ abstract class MatteryMenu( addSlot(slot) } - val mattery = player.matteryPlayer - - if (mattery != null && mattery.hasExopack) { - for (i in 0 until mattery.exopackContainer.containerSize) { - val slot = InventorySlot(mattery.exopackContainer, i) - - _playerInventorySlots.add(slot) - _playerExoSuitSlots.add(slot) - _playerCombinedInventorySlots.add(slot) - - mapQuickMove(slot, equipmentSlots) - mapQuickMoveToExternal(slot) - addSlot(slot) - } - + if (mattery.hasExopack) { _exopackChargeSlots.add(BatterySlot(mattery.exopackEnergy.parent, 0).also { mapQuickMoveToExternal(it); mapQuickMoveToInventory(it); addSlot(it) }) for (i in 0 until mattery.exopackChargeSlots.containerSize) @@ -400,6 +350,10 @@ abstract class MatteryMenu( exopackPowerLevel.with(mattery.exopackEnergy) } + + sortInventoryInput = PlayerInput(NullValueCodec) { + mattery.inventoryAndExopackNoHotbar.sort() + } } private var broadcastOnce = false @@ -471,7 +425,7 @@ abstract class MatteryMenu( val input: PlayerInput val field: IField - if (container is MatteryContainer) { + if (container is IMatteryContainer) { input = PlayerInput(ItemValueCodec.nullable, handler = { container.setSlotFilter(pSlot.slotIndex, it) }) field = mSynchronizer.ComputedField(getter = { container.getSlotFilter(pSlot.slotIndex) }, ItemValueCodec.nullable) } else { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt index ac20239f9..be03785a9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/Slots.kt @@ -11,6 +11,8 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.energy import ru.dbotthepony.mc.otm.client.minecraft +import ru.dbotthepony.mc.otm.container.IMatteryContainer +import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.runOnClient @@ -44,6 +46,26 @@ open class MatterySlot(container: Container, index: Int, x: Int = 0, y: Int = 0) open fun canTakeItemForPickAll(): Boolean { return true } + + override fun getMaxStackSize(): Int { + val container = container + + if (container is IMatteryContainer) { + return container.getMaxStackSize(slotIndex, ItemStack.EMPTY) + } else { + return super.getMaxStackSize() + } + } + + override fun getMaxStackSize(itemStack: ItemStack): Int { + val container = container + + if (container is IMatteryContainer) { + return container.getMaxStackSize(slotIndex, itemStack) + } else { + return super.getMaxStackSize(itemStack) + } + } } open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y), Predicate { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.kt index 08aeb6c19..fc10bffc0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.kt @@ -162,7 +162,7 @@ class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val isRemote: Bo val id2tuple = Int2ObjectOpenHashMap() val sortedView = ArrayList() - var sorter: Comparator = ItemStorageStackSorter.DEFAULT + var sorter: Comparator = ItemStorageStackSorter.DEFAULT set(value) { if (field != value) { field = value diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/CargoCrateMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/CargoCrateMenu.kt index df4e05718..159d23324 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/CargoCrateMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/decorative/CargoCrateMenu.kt @@ -5,6 +5,8 @@ import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity +import ru.dbotthepony.mc.otm.container.sort +import ru.dbotthepony.mc.otm.core.util.NullValueCodec import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.UserFilteredSlot @@ -16,9 +18,12 @@ class CargoCrateMenu @JvmOverloads constructor( tile: CargoCrateBlockEntity? = null ) : MatteryMenu(MMenus.CARGO_CRATE, p_38852_, inventory, tile) { val storageSlots: List - private val trackedPlayerOpen = !inventory.player.isSpectator + val sort = PlayerInput(NullValueCodec) { + tile?.container?.sort() + } + init { val container = tile?.container ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt index 702149d37..9a24e4021 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/DriveViewerMenu.kt @@ -55,7 +55,7 @@ class DriveViewerMenu( if (isAscending) { networkedItemView.sorter = sorting } else { - networkedItemView.sorter = sorting.reversed + networkedItemView.sorter = sorting.reversed() } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt index 82c13664c..0145d626b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/storage/ItemMonitorMenu.kt @@ -96,7 +96,7 @@ class ItemMonitorMenu( if (ascendingSort) { networkedItemView.sorter = sorting } else { - networkedItemView.sorter = sorting.reversed + networkedItemView.sorter = sorting.reversed() } } } diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.png index 6b7c5ba65030166100d8b301f4077f2dad46f020..fb22863c896707a6e8ab7444a05852b50448940b 100644 GIT binary patch delta 605 zcmV-j0;2u=2d4;-7YaxS1^@s6cDCY@u_40&e-sD`0Ni4;p#T5^BS}O-RCwC$o8hj5 zAPhzw@v(e--)sA|gb);3N(DS$a+AA78$6T`3U1pFLP)Gn{NX&$ujJC!?m)?-gsnK#_SQR6gLI^wE$VNAjyNK50b){bVeK_j_YA@yRs*@<2W80 ze}*ZH8aOfB7}{ zy^~|#UM>yfC9NaZ_*$#zwYt$*M@_-7M3;q zn(fK>BqRq*vJYK~BiSHY(xb07`K2HvaavDmm<)}Vb&?L~IvK76@yhTD`nOGXJIoM+hN=tk;iE#hF#1b}3^8f2nnE zyS3w<>hK@|@-*xb*2AWtb2b0fPZ6}0)XcEs1B~3CD5Ev|wAN+oN|_y`Ab{OrGF-un zM`>$VWoS;lytA^08DRIevM~Dt%+ki7UC2vL9PS?E>59=S5!~ME7%{W0e&?tHy;Aiz zQpZnol%@T(?2K~;LI@%BE?J#uM<8Vh4s0jTz;*-;YzNR(R*o4B>_*TMI{b_-7Ya5A1^@s6N1l!&u_40&e-Z>56rc;!H2?qs_(?=TRCwC$TS*SX zAPj6!p2hR|S9(H1h!AfqMl+|hO&l06*rxn`0002?NGUxjrDqp8UJ+&~t91>c2a(Jr zJ9~kpaz@Gek{f1_9Amulclnw+;TYo;W4!YHHTfK6q?DdgR{i`)Z@XubT#}b3*7}`7 ze}3s`0TbC0y>9s#vwX_VNTAT?}olwQ8KbxpEp$_pv?PzEirW~oKj z_tlmt9g8Y-^j>^g)*vC-By{xRC)Gtq$9n5=lw@%{krO&DZakAtZiSL&dV`qg7TFq- zP9)l_U}@#Q00000006+MT$)lX2T-*^e;y4?BwEuD*EH1YXS6Lhbx@@#dh%a|h zC`7*9N0Fm7sxRDBvaHFc_O$EmCvK;WDu>e`Ixie*gf0 zb^f?Yj#mU~Dq}^GwzsWS+$x0!4k4?_-XR}0CEDUh#8)Ndne6&F2IY80;FOk@jUexp zo{2?&9f<(ho0Z_6vW(GV4X3!K>-5elJa`~`bSMknC*Wm_L7K>Ob{u*WSzR&CGK6XW z>)6q;M1&07*qoM6N<$f~^h) A%>V!Z diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.xcf b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/storage_controls.xcf index 1a198317e90923ab36dde4eea5886a4151261e5e..96fc5f0f62dc8aad9d610b947921602e7f091fa2 100644 GIT binary patch delta 1356 zcmbtTJ!lj`6rOpzo4wt;OL7-4Yi=*QmkTk9h*%^_Lcm~S#GsKV*qFvB78WTif`*I1 zMo8f?8ch*U3&A2GI3R)$EHe+o547~jm?MYNJ+;Ja_;oA=($H}m$@ z@r9GOa^X>;#~e}yK`H)Z5eE^yw6cGgo~ulKD*wPG4s%j-!$ZxEuQ~ZtbK?!onGww` z-!-?M)!a6u*}tMWsO5)`^}6Gx=Faz;cgZv1U~9C^^Wq2bN_DzA6F;j?$1CxZc)2=L zon;j6vBL$E_OiA1{!HDGP9#Q}RSJ19_OKDRwQLO5WU`wyLZLgDEa7@>39G z2^qA$IX6{7hZu@o0xWa__KT+3oy;tohZEFd6ImauIeDdEE%CrG0Bo)<+J384`y&gC zR({zE0o*|CxP7?eB~H~|fQ5-gR!p4XZxRVwKot@BInJV4WJr8X@qwdp0Q zgOpD;j#9{usjci)xs}Ya`n&FBA4)CkYsqBSniu$s=4A>wP@%biecR#DUj8V%55OB& zqIX@D_U5^hTLQ=rDK$T?)ch3x?Ck_7+(p%=T#1F(ygxq*5O%20a6qZyX}(%GPoemn z(V|b^DY5v4j|KNBlqzhZIM#BW4gqSQrKj&L2aDN z=Cg^2fKY_Mrj@#bF%)BUqvFd)17wTY7qHAW(l@hBks_uDh>Z})(o4^oKOYYGQDEdtv_ut;SUQj4Ux1Pg;WdHd#K@(<`RZ@xR9H;?yy`+8w>_IWwH z%Jk}?co>}H;bUCtUY_IA-PIo}-|>bk6KWk2TC=;%UYcO?W9Ha~@z%%!DBrc@Y@_9~V1TMSe1OIhYfa zFmGX^0_J_WV_gBTdPJ=cM6ECKw%q}+W1{vwqIO62b87%@j;MQ%sCymh442%y%c1a% zQiXj=6&?A^Ujpunj#&l2N6=rFljZXOBir<}sBHEazvc{vMvSM^oPDSXpnWYEK zHkCDF`MGrsptVf2^(v)K>;2I$j>QAFa%!0Igb%`EDgpckR7dpv(u-2Yh}pk*_@_v% z6}G0p)(Tr=DCLMZrGUq>I392~(ZdjqH|(AwE1^zw5bj-hZ}7ww5iahirFDhAlJG8