Slice container APIs and decouple interface from implementation, container sorting test

This commit is contained in:
DBotThePony 2023-08-20 15:39:20 +07:00
parent 67f97dfba6
commit d4c029f27d
Signed by: DBot
GPG Key ID: DCC23B5715498507
38 changed files with 845 additions and 636 deletions

View File

@ -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")

View File

@ -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")

View File

@ -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()
// удаляем по одному предмету из сетки крафта

View File

@ -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)

View File

@ -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<ItemStack> {
val iterators = ArrayList<Iterator<ItemStack>>()
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<Iterator<ItemStack>>()
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<ItemStack> {
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<ItemStack> {
fun Player.trackedItems(includeCosmetics: Boolean = true): Iterator<ItemStack> {
if (containerMenu == inventoryMenu || containerMenu == matteryPlayer?.exoPackMenu) {
return items(includeCosmetics)
}
val seen = ReferenceOpenHashSet<ItemStack>(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 })
}
/**

View File

@ -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 }

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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<Exopack
scrollPanel.dock = Dock.RIGHT
scrollPanel.setDockMargin(right = 3f)
val closeButtonPanel = LargeRectangleButtonPanel(this, frame, x = frame.width - 2f - LargeRectangleButtonPanel.SIZE, y = -LargeRectangleButtonPanel.SIZE - 2f, skinElement = Widgets18.RETURN_ARROW_LEFT, onPress = {
val closeButtonPanel = LargeRectangleButtonPanel(this, frame, x = frame.width - 2f - LargeRectangleButtonPanel.SIZE, y = -LargeRectangleButtonPanel.SIZE - 2f, icon = Widgets18.RETURN_ARROW_LEFT, onPress = {
shouldOpenVanillaInventory = true
val minecraft = minecraft!!
@ -256,14 +257,28 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<Exopack
}).also { it.tooltips.add(TranslatableComponent("otm.gui.exopack.go_back")) }
if (isCuriosLoaded) {
LargeRectangleButtonPanel(this, frame, x = closeButtonPanel.x - 2f - LargeRectangleButtonPanel.SIZE, y = closeButtonPanel.y, skinElement = Widgets18.CURIOS_INVENTORY, onPress = {
LargeRectangleButtonPanel(this, frame, x = closeButtonPanel.x - 2f - LargeRectangleButtonPanel.SIZE, y = closeButtonPanel.y, icon = Widgets18.CURIOS_INVENTORY, onPress = {
openCuriosScreen(minecraft!!.player!!.containerMenu.carried)
}).also { it.tooltips.add(TranslatableComponent("otm.gui.exopack.go_curios")) }
}
makeInventoryRowsControls(frame, frame.width + 2f, frame.height.coerceAtMost(95f)) { movePixels ->
val controls = DeviceControls(this, frame)
controls.dockTop = 85f
controls.addButton(makeInventoryRowsControls(frame) { movePixels ->
frame.y += movePixels
moveMousePosScaled(y = movePixels)
} as EditablePanel<ExopackInventoryScreen>)
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

View File

@ -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<T : MatteryMenu>(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<MatteryScreen<*>> {
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<T : MatteryMenu>(menu: T, inventory: Inventory, tit
init {
if (menu.playerInventorySlots.isNotEmpty() && menu.autoCreateInventoryFrame) {
if (menu.playerExoSuitSlots.isEmpty()) {
val deviceControls: DeviceControls<MatteryScreen<*>>
if (menu.playerCombinedInventorySlots.size <= 27) {
inventoryFrame = FramePanel<MatteryScreen<*>>(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<T : MatteryMenu>(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<T : MatteryMenu>(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()) {

View File

@ -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<CargoCrateMenu>(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
}
}

View File

@ -110,7 +110,7 @@ class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) :
buttons.clear()
for (recipe in menu.possibleRecipes) {
object : LargeRectangleButtonPanel<PainterScreen>(this@PainterScreen, canvas.canvas, skinElement = ItemStackIcon(recipe.output, 14f, 14f).fixed()) {
object : LargeRectangleButtonPanel<PainterScreen>(this@PainterScreen, canvas.canvas, icon = ItemStackIcon(recipe.output, 14f, 14f).fixed()) {
init {
buttons.add(this)
dockRight = 1f

View File

@ -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"),

View File

@ -386,7 +386,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
if (upgrades != null) {
upgradesButton = addButton(object : LargeRectangleButtonPanel<S>(
screen, this@DeviceControls,
skinElement = Widgets18.UPGRADES
icon = Widgets18.UPGRADES
) {
init {
tooltips.add(TranslatableComponent("otm.gui.upgrades"))
@ -473,7 +473,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
}
if (itemConfig != null) {
itemConfigButton = addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls, skinElement = Widgets18.ITEMS_CONFIGURATION) {
itemConfigButton = addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls, icon = Widgets18.ITEMS_CONFIGURATION) {
init {
tooltips.add(TranslatableComponent("otm.gui.sides.item_config"))
}
@ -492,7 +492,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
}
if (energyConfig != null) {
energyConfigButton = addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls, y = nextY, skinElement = Widgets18.ENERGY_CONFIGURATION) {
energyConfigButton = addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls, y = nextY, icon = Widgets18.ENERGY_CONFIGURATION) {
init {
tooltips.add(TranslatableComponent("otm.gui.sides.energy_config"))
}
@ -511,7 +511,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
}
if (fluidConfig != null) {
fluidConfigButton = addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls, y = nextY, skinElement = Widgets18.FLUID_CONFIGURATION) {
fluidConfigButton = addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls, y = nextY, icon = Widgets18.FLUID_CONFIGURATION) {
init {
tooltips.add(TranslatableComponent("otm.gui.sides.fluid_config"))
}
@ -530,10 +530,28 @@ class DeviceControls<out S : MatteryScreen<*>>(
}
}
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 {

View File

@ -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<out S : Screen>(
screen: S,
@ -15,8 +17,8 @@ open class LargeRectangleButtonPanel<out S : Screen>(
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<S>(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<out S : Screen>(
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 <S : Screen> input(
screen: S,
parent: EditablePanel<*>?,
input: MatteryMenu.PlayerInput<Nothing?>,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
icon: IGUIRenderable? = null,
iconWinding: UVWindingOrder? = null,
): LargeRectangleButtonPanel<S> {
return object : LargeRectangleButtonPanel<S>(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) {}
}
}
}
}

View File

@ -93,7 +93,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
outputs.dockResize = DockResizeMode.NONE
outputs.dockMargin = DockProperty(bottom = 3f)
object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, inputs, skinElement = STORE_1) {
object : LargeRectangleButtonPanel<EssenceStorageScreen>(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<EssenceStorageScreen>(this@EssenceStorageScreen, inputs, skinElement = STORE_10) {
object : LargeRectangleButtonPanel<EssenceStorageScreen>(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<EssenceStorageScreen>(this@EssenceStorageScreen, inputs, skinElement = STORE_ALL) {
object : LargeRectangleButtonPanel<EssenceStorageScreen>(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<EssenceStorageScreen>(this@EssenceStorageScreen, outputs, skinElement = DISPENSE_1) {
object : LargeRectangleButtonPanel<EssenceStorageScreen>(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<EssenceStorageScreen>(this@EssenceStorageScreen, outputs, skinElement = DISPENSE_10) {
object : LargeRectangleButtonPanel<EssenceStorageScreen>(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<EssenceStorageScreen>(this@EssenceStorageScreen, outputs, skinElement = DISPENSE_ALL) {
object : LargeRectangleButtonPanel<EssenceStorageScreen>(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<EssenceStorageScreen>(this@EssenceStorageScreen, customBar, skinElement = STORE_CUSTOM) {
object : LargeRectangleButtonPanel<EssenceStorageScreen>(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<EssenceStorageScreen>(this@EssenceStorageScreen, customBar, skinElement = DISPENSE_CUSTOM) {
object : LargeRectangleButtonPanel<EssenceStorageScreen>(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<EssenceStorageScreen>(this@EssenceStorageScreen, customBar, skinElement = SET_EXACT) {
object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, customBar, icon = SET_EXACT) {
init {
tooltips.add(TranslatableComponent("otm.gui.experience.set_exact", customDispense))
dock = Dock.RIGHT

View File

@ -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<Pair<Container, Iterator<Int>>>) : Container, IIterableContainer {
constructor(vararg containers: Container) : this(containers.stream().map { it to (0 until it.containerSize).iterator() })
constructor(containers: Collection<Container>) : this(containers.stream().map { it to (0 until it.containerSize).iterator() })
class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IMatteryContainer {
constructor(vararg containers: Container) : this(containers.stream().map { it to (0 until it.containerSize) })
constructor(containers: Collection<Container>) : this(containers.stream().map { it to (0 until it.containerSize) })
private val slots: List<ContainerSlot>
private val slotsMap: Map<Container, List<ContainerSlot>>
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<Slot>
private val slotsMap: Map<Container, List<IContainerSlot>>
private val containers: Set<Container>
private val fullCoverage: List<Container>
private val notFullCoverage: Map<Container, List<ContainerSlot>>
private val notFullCoverage: Map<Container, List<IContainerSlot>>
init {
val list = ImmutableList.Builder<ContainerSlot>()
val list = ImmutableList.Builder<Slot>()
var i = 0
val validationMap = Reference2ObjectOpenHashMap<Container, IntSet>()
val slotsMap = Reference2ObjectOpenHashMap<Container, ArrayList<ContainerSlot>>()
val slotsMap = Reference2ObjectOpenHashMap<Container, ArrayList<IContainerSlot>>()
for ((container, slots) in containers) {
val validator = validationMap.computeIfAbsent(container, Object2ObjectFunction { IntAVLTreeSet() })
@ -46,9 +60,8 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterator<Int>>>) : 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<Pair<Container, Iterator<Int>>>) : 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<Pair<Container, Iterator<Int>>>) : 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<Pair<Container, Iterator<Int>>>) : 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<Pair<Container, Iterator<Int>>>()
private val values = ArrayList<Pair<Container, Iterable<Int>>>()
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<Int>): 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<Int>): 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<net.minecraft.world.inventory.Slot>): CombinedContainer {
val builder = Builder()
for (slot in slots) {
builder.add(slot.container, slot.slotIndex)
}
return builder.build()
}
}
}

View File

@ -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

View File

@ -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<IContainerSlot> {
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<ItemStack> = 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) }
}

View File

@ -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<Container>) : 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<Item>): Boolean {
return toProxy.get().hasAnyOf(items)
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return toProxy.get().hasAnyMatching(predicate)
}
}

View File

@ -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)

View File

@ -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<ItemStack> {
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<ItemStack> {
return iterator(true)
}
/**
* Iterates non-empty slots of this container
*/
fun slotIterator(): Iterator<IContainerSlot> {
return slotIterator(true)
}
fun iterator(nonEmpty: Boolean): Iterator<ItemStack> {
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<IContainerSlot> {
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
}
}

View File

@ -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<ItemStack>, INBTSerializable<Tag?>, StackedContentsCompatible {
open class MatteryContainer(protected val watcher: Runnable, private val size: Int) : IMatteryContainer, INBTSerializable<Tag?>, 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<IContainerSlot> {
indicesReferenced = true
return nonEmptyIndices.iterator().map { ContainerSlot(it, this) }
return nonEmptyIndices.iterator().map { Slot(it) }
}
final override fun slotIterator(nonEmpty: Boolean): kotlin.collections.Iterator<IContainerSlot> {
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()
}

View File

@ -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<ItemStack> {
val slot: Int
val container: Container
@ -18,28 +24,23 @@ interface IContainerSlot : GetterSetter<ItemStack> {
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<ItemStack> {
}
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<ItemStack> {
/**
* Iterates over non-empty itemstacks of this container
*/
override fun iterator(): Iterator<ItemStack> {
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<ItemStack>
/**
* Iterates non-empty slots of this container
*/
fun slotIterator(): Iterator<IContainerSlot> {
return slotIterator(true)
}
fun slotIterator(nonEmpty: Boolean): Iterator<IContainerSlot>
}
operator fun Container.iterator() = iterator(true)
fun Container.iterator(nonEmpty: Boolean): Iterator<ItemStack> {
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<ItemStack> {
}
fun Container.slotIterator(nonEmpty: Boolean = true): Iterator<IContainerSlot> {
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) }

View File

@ -362,17 +362,12 @@ fun <T> Stream<T>.asIterable(): Iterable<T> {
}
}
// 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 <T> Comparator.nullsFirst(original: Comparator<in T>): Comparator<in T?>
fun <T> Comparator<in T>.nullsFirst(): Comparator<T?> {
return Comparator.nullsFirst(this as Comparator<in T?>)
fun <T> Comparator<T>.nullsFirst(): Comparator<T> {
return Comparator.nullsFirst(this)
}
fun <T> Comparator<in T>.nullsLast(): Comparator<T?> {
return Comparator.nullsLast(this as Comparator<in T?>)
fun <T> Comparator<T>.nullsLast(): Comparator<T> {
return Comparator.nullsLast(this)
}
class MappedComparator<T, O>(private val parent: Comparator<O>, private val mapper: (T) -> O) : Comparator<T> {

View File

@ -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()
}
}
}
}

View File

@ -294,8 +294,8 @@ fun <T, A, R> Iterator<T>.collect(collector: Collector<T, A, R>): R {
return collector.finisher().apply(instance)
}
fun <T> Iterator<T>.toList(): MutableList<T> {
val result = ArrayList<T>()
fun <T> Iterator<T>.toList(expectedSize: Int = 16): MutableList<T> {
val result = ArrayList<T>(expectedSize)
result.addAll(this)
return result
}

View File

@ -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<Item?>.stacks(): Comparator<ItemStack?> {
return Comparator<ItemStack> { o1, o2 -> this@stacks.compare(o1.item, o2.item) }.nullsFirst()
private fun Comparator<Item>.stacks(): Comparator<ItemStack> {
return Comparator { o1, o2 -> this@stacks.compare(o1.item, o2.item) }
}
private fun Comparator<Item?>.storage(): Comparator<ItemStorageStack?> {
return Comparator<ItemStorageStack> { o1, o2 -> this@storage.compare(o1.item, o2.item) }.nullsFirst()
private fun Comparator<Item>.storage(): Comparator<ItemStorageStack> {
return Comparator { o1, o2 -> this@storage.compare(o1.item, o2.item) }
}
object CreativeMenuItemComparator : Comparator<Item> {
@ -156,7 +156,7 @@ object ItemStorageStackCountComparator : Comparator<ItemStorageStack> {
val NullsLast = nullsLast()
}
enum class ItemSorter(comparator: Comparator<Item>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<Item?> by comparator.nullsFirst() {
enum class ItemSorter(comparator: Comparator<Item>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<Item> 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<Item>, private val sTitle: Componen
val icon: IGUIRenderable by icon
val title: MutableComponent get() = sTitle.copy()
val suppliers = suppliers()
val reversed: Comparator<Item?> = reversed()
}
enum class ItemStackSorter(comparator: Comparator<ItemStack?>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<ItemStack?> by comparator {
enum class ItemStackSorter(comparator: Comparator<ItemStack>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<ItemStack> 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<ItemStack?>, private val sTitl
val icon: IGUIRenderable by icon
val title: MutableComponent get() = sTitle.copy()
val suppliers = suppliers()
val reversed: Comparator<ItemStack?> = reversed()
}
enum class ItemStorageStackSorter(comparator: Comparator<ItemStorageStack?>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<ItemStorageStack?> by comparator {
enum class ItemStorageStackSorter(comparator: Comparator<ItemStorageStack>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<ItemStorageStack> 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<ItemStorageStack?>, pri
val icon: IGUIRenderable by icon
val title: MutableComponent get() = sTitle.copy()
val suppliers = suppliers()
val reversed: Comparator<ItemStorageStack?> = reversed()
}

View File

@ -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<UUID>(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)) {

View File

@ -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<InventorySlot>()
private val _playerHotbarSlots = ArrayList<InventorySlot>()
private val _playerMainSlots = ArrayList<InventorySlot>()
private val _playerExoSuitSlots = ArrayList<InventorySlot>()
private val _playerCombinedInventorySlots = ArrayList<InventorySlot>()
private val _exopackChargeSlots = ArrayList<MatterySlot>()
@ -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<InventorySlot> = Collections.unmodifiableList(_playerInventorySlots)
@ -199,17 +198,7 @@ abstract class MatteryMenu(
val playerHotbarSlots: List<InventorySlot> = Collections.unmodifiableList(_playerHotbarSlots)
/**
* inventory only
*/
val playerMainSlots: List<InventorySlot> = Collections.unmodifiableList(_playerMainSlots)
/**
* exosuit only
*/
val playerExoSuitSlots: List<InventorySlot> = Collections.unmodifiableList(_playerExoSuitSlots)
/**
* inventory + exosuit (in this order)
* inventory + Exopack (in this order)
*/
val playerCombinedInventorySlots: List<InventorySlot> = Collections.unmodifiableList(_playerCombinedInventorySlots)
@ -217,6 +206,9 @@ abstract class MatteryMenu(
val exopackPowerLevel = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(mSynchronizer)
var sortInventoryInput: PlayerInput<Nothing?>? = 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<Boolean>? = 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<Item?>
val field: IField<Item?>
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 {

View File

@ -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<ItemStack> {

View File

@ -162,7 +162,7 @@ class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val isRemote: Bo
val id2tuple = Int2ObjectOpenHashMap<Tuple>()
val sortedView = ArrayList<Tuple>()
var sorter: Comparator<in ItemStorageStack> = ItemStorageStackSorter.DEFAULT
var sorter: Comparator<ItemStorageStack> = ItemStorageStackSorter.DEFAULT
set(value) {
if (field != value) {
field = value

View File

@ -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<UserFilteredSlot>
private val trackedPlayerOpen = !inventory.player.isSpectator
val sort = PlayerInput(NullValueCodec) {
tile?.container?.sort()
}
init {
val container = tile?.container ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY)

View File

@ -55,7 +55,7 @@ class DriveViewerMenu(
if (isAscending) {
networkedItemView.sorter = sorting
} else {
networkedItemView.sorter = sorting.reversed
networkedItemView.sorter = sorting.reversed()
}
}
}

View File

@ -96,7 +96,7 @@ class ItemMonitorMenu(
if (ascendingSort) {
networkedItemView.sorter = sorting
} else {
networkedItemView.sorter = sorting.reversed
networkedItemView.sorter = sorting.reversed()
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1020 B

After

Width:  |  Height:  |  Size: 1.0 KiB