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("balance_inputs", "Balance input slots")
gui("sorting.sort_now", "Sort")
gui("sorting.default", "Default sorting") gui("sorting.default", "Default sorting")
gui("sorting.name", "Sort by name") gui("sorting.name", "Sort by name")
gui("sorting.id", "Sort by ID") gui("sorting.id", "Sort by ID")

View File

@ -805,6 +805,7 @@ private fun gui(provider: MatteryLanguageProvider) {
gui("balance_inputs", "Балансировать входные слоты") gui("balance_inputs", "Балансировать входные слоты")
gui("sorting.sort_now", "Отсортировать")
gui("sorting.default", "Сортировка по умолчанию") gui("sorting.default", "Сортировка по умолчанию")
gui("sorting.name", "Сортировка по имени") gui("sorting.name", "Сортировка по имени")
gui("sorting.id", "Сортировка по ID") gui("sorting.id", "Сортировка по ID")

View File

@ -43,8 +43,6 @@ import java.util.*
import kotlin.collections.HashMap import kotlin.collections.HashMap
import ru.dbotthepony.mc.otm.client.render.Widgets8 import ru.dbotthepony.mc.otm.client.render.Widgets8
import ru.dbotthepony.mc.otm.container.CombinedContainer 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.container.util.slotIterator
import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.toList 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}" } 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() val copy = craftingGrid.iterator(true).map { it.copy() }.toList()
// удаляем по одному предмету из сетки крафта // удаляем по одному предмету из сетки крафта

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.block.entity.tech package ru.dbotthepony.mc.otm.block.entity.tech
import it.unimi.dsi.fastutil.ints.IntArrayList
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
@ -74,7 +75,7 @@ class PlatePressBlockEntity(
if (status.job.itemStack.isEmpty) if (status.job.itemStack.isEmpty)
return status.success() 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() return status.noItem()
experience = (experience + status.experience).coerceAtMost(100.0) experience = (experience + status.experience).coerceAtMost(100.0)

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.mc.otm.capability package ru.dbotthepony.mc.otm.capability
import com.google.common.collect.Streams import com.google.common.collect.Streams
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.network.chat.Component 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.getMekanismEnergySided
import ru.dbotthepony.mc.otm.compat.mekanism.mekanismEnergy import ru.dbotthepony.mc.otm.compat.mekanism.mekanismEnergy
import ru.dbotthepony.mc.otm.container.util.awareStream 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.container.util.iterator
import ru.dbotthepony.mc.otm.core.collect.AwareItemStack import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.core.collect.ContainerItemStackEntry import ru.dbotthepony.mc.otm.core.collect.ContainerItemStackEntry
import ru.dbotthepony.mc.otm.core.collect.concatIterators 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.filter
import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
@ -217,21 +216,16 @@ fun ICapabilityProvider.getMatteryEnergySided(side: Direction? = null): LazyOpti
return LazyOptional.empty() return LazyOptional.empty()
} }
/**
* DO NOT modify returned ItemStacks!
*
* Contains all items that player might carry
*/
fun Player.items(includeCosmetics: Boolean = true): Iterator<ItemStack> { fun Player.items(includeCosmetics: Boolean = true): Iterator<ItemStack> {
val iterators = ArrayList<Iterator<ItemStack>>() val matteryPlayer = matteryPlayer ?: return emptyIterator()
iterators.add(inventory.iterator())
matteryPlayer?.let { val iterators = ArrayList<Iterator<ItemStack>>()
if (it.hasExopack) { iterators.add(matteryPlayer.wrappedInventory.slotIterator().filter { !it.isForbiddenForAutomation }.map { it.item })
iterators.add(it.exopackContainer.iterator())
iterators.add(it.exopackEnergy.parent.iterator()) if (matteryPlayer.hasExopack) {
iterators.add(it.exopackChargeSlots.iterator()) iterators.add(matteryPlayer.exopackContainer.slotIterator().filter { !it.isForbiddenForAutomation }.map { it.item })
} iterators.add(matteryPlayer.exopackEnergy.parent.iterator())
iterators.add(matteryPlayer.exopackChargeSlots.iterator())
} }
if (isCuriosLoaded) { if (isCuriosLoaded) {
@ -245,25 +239,12 @@ fun Player.items(includeCosmetics: Boolean = true): Iterator<ItemStack> {
return concatIterators(iterators) return concatIterators(iterators)
} }
/** fun Player.trackedItems(includeCosmetics: Boolean = true): Iterator<ItemStack> {
* 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> {
if (containerMenu == inventoryMenu || containerMenu == matteryPlayer?.exoPackMenu) { if (containerMenu == inventoryMenu || containerMenu == matteryPlayer?.exoPackMenu) {
return items(includeCosmetics) return items(includeCosmetics)
} }
val seen = ReferenceOpenHashSet<ItemStack>(containerMenu.slots.size) return concatIterators(items(includeCosmetics), containerMenu.slots.iterator().map { it.item }.filter { it.isNotEmpty })
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 })
} }
/** /**

View File

@ -10,7 +10,6 @@ import net.minecraft.client.player.AbstractClientPlayer
import net.minecraft.commands.Commands import net.minecraft.commands.Commands
import net.minecraft.commands.arguments.EntityArgument import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.nbt.ByteTag
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.IntTag import net.minecraft.nbt.IntTag
import net.minecraft.nbt.ListTag 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.boss.wither.WitherBoss
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import net.minecraft.world.item.ProjectileWeaponItem 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.CombinedContainer
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.get 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.util.slotIterator
import ru.dbotthepony.mc.otm.container.vanishCursedItems import ru.dbotthepony.mc.otm.container.vanishCursedItems
import ru.dbotthepony.mc.otm.core.* 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.Decimal
import ru.dbotthepony.mc.otm.core.math.RGBAColor import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.core.math.minus 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.getCompoundList
import ru.dbotthepony.mc.otm.core.nbt.getIntList import ru.dbotthepony.mc.otm.core.nbt.getIntList
import ru.dbotthepony.mc.otm.core.nbt.getStringList import ru.dbotthepony.mc.otm.core.nbt.getStringList
@ -258,27 +259,23 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
synchronizer.Field(null, ItemValueCodec.nullable) synchronizer.Field(null, ItemValueCodec.nullable)
} }
val regularSlotChargeFlag = immutableList(Inventory.INVENTORY_SIZE + 5) { val slotsChargeFlag by synchronizer.Set(
synchronizer.bool() codec = VarIntValueCodec,
} backingSet = IntAVLTreeSet(),
)
private fun slotChargeToDefault() { private fun slotChargeToDefault() {
// броня // броня
regularSlotChargeFlag[36].boolean = true slotsChargeFlag.add(36)
regularSlotChargeFlag[37].boolean = true slotsChargeFlag.add(37)
regularSlotChargeFlag[38].boolean = true slotsChargeFlag.add(38)
regularSlotChargeFlag[39].boolean = true slotsChargeFlag.add(39)
} }
init { init {
slotChargeToDefault() slotChargeToDefault()
} }
val exoPackSlotsChargeFlag by synchronizer.Set(
codec = VarIntValueCodec,
backingSet = IntAVLTreeSet(),
)
/** /**
* Exopack container, which actually store items inside Exopack * Exopack container, which actually store items inside Exopack
*/ */
@ -303,19 +300,60 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
value.addFilterSynchronizer(synchronizer) value.addFilterSynchronizer(synchronizer)
field = value 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 _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 val combinedInventory: CombinedContainer
get() { get() {
if (_combinedInventory == null) if (_combinedInventory == null)
_combinedInventory = CombinedContainer(ply.inventory, exopackContainer) _combinedInventory = CombinedContainer(wrappedInventory, exopackContainer)
return _combinedInventory!! 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 * Whenever Exopack has 3x3 crafting grid upgrade installed
*/ */
@ -914,14 +952,8 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
} }
} }
tag["regularSlotChargeFlag"] = ListTag().also { tag["slotsChargeFlag"] = ListTag().also {
for (flag in regularSlotChargeFlag) { for (value in slotsChargeFlag) {
it.add(ByteTag.valueOf(flag.boolean))
}
}
tag["exoPackSlotsChargeFlag"] = ListTag().also {
for (value in exoPackSlotsChargeFlag) {
it.add(IntTag.valueOf(value)) it.add(IntTag.valueOf(value))
} }
} }
@ -945,12 +977,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
filter.value = null filter.value = null
} }
for (flag in regularSlotChargeFlag) { slotsChargeFlag.clear()
flag.boolean = false
}
exoPackSlotsChargeFlag.clear()
slotChargeToDefault() slotChargeToDefault()
val regularSlotFilters = tag.getStringList("regularSlotFilters") 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 this.regularSlotFilters[i].value = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(path) ?: continue) ?: Items.AIR
} }
val regularSlotChargeFlag = tag.getByteList("regularSlotChargeFlag") for (v in tag.getIntList("slotsChargeFlag")) {
this.slotsChargeFlag.add(v.asInt)
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)
} }
// iterations // iterations
@ -1128,25 +1149,9 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
} }
if (available.isPositive) { if (available.isPositive) {
for ((i, flag) in regularSlotChargeFlag.withIndex()) { for (slot in slotsChargeFlag) {
if (flag.boolean) { if (slot in 0 until combinedInventory.containerSize) {
val item = ply.inventory[i] val item = combinedInventory[slot]
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]
if (item.isNotEmpty) { if (item.isNotEmpty) {
item.energy?.let { 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) * This re-implement [Inventory.add] logic (original method is redirected to this)
*/ */
fun inventoryAddImpl(stack: ItemStack): Boolean { fun inventoryAddImpl(stack: ItemStack): Boolean {
val items = ply.inventory.items inventoryAndExopack.consumeItem(stack, false, popTime = 5)
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
}
}
}
MinecraftForge.EVENT_BUS.post(ItemStackLeftoverEvent(stack, this)) MinecraftForge.EVENT_BUS.post(ItemStackLeftoverEvent(stack, this))
if (ply.abilities.instabuild) { 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 { init {
WitherBoss.TARGETING_CONDITIONS.selector(WitherBoss.TARGETING_CONDITIONS.selector!!.and { it.matteryPlayer?.isAndroid != true }) 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 } 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 screen = if (eventScreen is AbstractContainerScreen<*> && (eventScreen.menu is InventoryMenu || eventScreen.isCosmeticArmorScreen)) eventScreen else return
val widget = Panel2Widget(LargeRectangleButtonPanel(screen, null, val widget = Panel2Widget(LargeRectangleButtonPanel(screen, null,
x = screen.guiLeft + screen.xSize - 2f - LargeRectangleButtonPanel.SIZE, x = screen.guiLeft + screen.xSize - 2f - LargeRectangleButtonPanel.SIZE,
y = screen.guiTop.toFloat() - LargeRectangleButtonPanel.SIZE - 2, y = screen.guiTop.toFloat() - LargeRectangleButtonPanel.SIZE - 2,
skinElement = Widgets18.RETURN_ARROW_LEFT, icon = Widgets18.RETURN_ARROW_LEFT,
skinElementWinding = UVWindingOrder.FLOP, iconWinding = UVWindingOrder.FLOP,
onPress = { onPress = {
shouldOpenVanillaInventory = false shouldOpenVanillaInventory = false
val mouseX = minecraft.mouseHandler.xpos() val mouseX = minecraft.mouseHandler.xpos()
val mouseY = minecraft.mouseHandler.ypos() val mouseY = minecraft.mouseHandler.ypos()

View File

@ -6,7 +6,7 @@ import ru.dbotthepony.mc.otm.client.render.sprites.MatteryAtlas
object WidgetLocation { object WidgetLocation {
val LARGE_BUTTON = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/large_button.png"), 72f, 18f) 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 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) 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_STRETCHABLE = makeButton(buttonGrids)
val BUTTON_DISABLED = buttonGrids.next() 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_DOWN = storageGrid.next()
val ARROW_UP = storageGrid.next() val ARROW_UP = storageGrid.next()
val SORT_DEFAULT = storageGrid.next() val SORT_DEFAULT = storageGrid.next()
@ -53,6 +53,7 @@ object Widgets18 {
val PAUSE = storageGrid.next() val PAUSE = storageGrid.next()
val PLAY = storageGrid.next() val PLAY = storageGrid.next()
val STOP = storageGrid.next() val STOP = storageGrid.next()
val SORT_NOW = storageGrid.next()
private val miscGrid = WidgetLocation.MISC_18.grid(4, 4) 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.core.TranslatableComponent
import ru.dbotthepony.mc.otm.client.render.sprites.sprite 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.*
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.button.LargeRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.InventorySlotPanel 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.dock = Dock.RIGHT
scrollPanel.setDockMargin(right = 3f) 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 shouldOpenVanillaInventory = true
val minecraft = minecraft!! val minecraft = minecraft!!
@ -256,14 +257,28 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<Exopack
}).also { it.tooltips.add(TranslatableComponent("otm.gui.exopack.go_back")) } }).also { it.tooltips.add(TranslatableComponent("otm.gui.exopack.go_back")) }
if (isCuriosLoaded) { 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) openCuriosScreen(minecraft!!.player!!.containerMenu.carried)
}).also { it.tooltips.add(TranslatableComponent("otm.gui.exopack.go_curios")) } }).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 frame.y += movePixels
moveMousePosScaled(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 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.Widgets18
import ru.dbotthepony.mc.otm.client.render.translation import ru.dbotthepony.mc.otm.client.render.translation
import ru.dbotthepony.mc.otm.client.screen.panels.* 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.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.InventorySlotPanel 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 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) { return HeightControls(this, parent, x, y) {
inventoryRows += if (it) 1 else -1 inventoryRows += if (it) 1 else -1
callback.invoke(if (it) -AbstractSlotPanel.SIZE / 2f else AbstractSlotPanel.SIZE / 2f) 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 { init {
if (menu.playerInventorySlots.isNotEmpty() && menu.autoCreateInventoryFrame) { 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 = FramePanel<MatteryScreen<*>>(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH, INVENTORY_FRAME_HEIGHT, inventory.displayName).also(this::addPanel)
inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().also { inventoryFrame!!.makeHelpButton().addSlotFiltersHelp().also {
if (menu.player.matteryPlayer?.hasExopack == true) if (menu.player.matteryPlayer?.hasExopack == true)
it.tooltips.add(TranslatableComponent("otm.gui.help.slot_charging")) it.tooltips.add(TranslatableComponent("otm.gui.help.slot_charging"))
} }
deviceControls = DeviceControls(this, inventoryFrame!!)
val hotbarStrip = EditablePanel(this, inventoryFrame, height = AbstractSlotPanel.SIZE) val hotbarStrip = EditablePanel(this, inventoryFrame, height = AbstractSlotPanel.SIZE)
hotbarStrip.dock = Dock.BOTTOM 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")) it.tooltips.add(TranslatableComponent("otm.gui.help.slot_charging"))
} }
deviceControls = DeviceControls(this, inventoryFrame!!)
inventoryScrollbar = DiscreteScrollBarPanel(this, inventoryFrame, { integerDivisionDown(menu.playerCombinedInventorySlots.size, 9) }, { inventoryScrollbar = DiscreteScrollBarPanel(this, inventoryFrame, { integerDivisionDown(menu.playerCombinedInventorySlots.size, 9) }, {
_, old, new -> _, 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 } mainFrame?.let { it.y += movePixels }
inventoryFrame?.let { it.y += movePixels } inventoryFrame?.let { it.y += movePixels }
moveMousePosScaled(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()) { 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.network.chat.Component
import net.minecraft.world.entity.player.Inventory 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.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel 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.util.GridPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel 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.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu
class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Component) : MatteryScreen<CargoCrateMenu>(menu, inventory, title) { 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) for (slot in menu.storageSlots)
UserFilteredSlotPanel.of(this, grid, slot) 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 return frame
} }
} }

View File

@ -110,7 +110,7 @@ class PainterScreen(menu: PainterMenu, inventory: Inventory, title: Component) :
buttons.clear() buttons.clear()
for (recipe in menu.possibleRecipes) { 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 { init {
buttons.add(this) buttons.add(this)
dockRight = 1f dockRight = 1f

View File

@ -77,7 +77,7 @@ class MatterPanelScreen(
LargeRectangleButtonPanel( LargeRectangleButtonPanel(
this, this,
frame, frame,
skinElement = Widgets18.STOP, icon = Widgets18.STOP,
onPress = { onPress = {
frame.queryUser( frame.queryUser(
TranslatableComponent("otm.gui.matter_panel.cancel_all"), TranslatableComponent("otm.gui.matter_panel.cancel_all"),

View File

@ -386,7 +386,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
if (upgrades != null) { if (upgrades != null) {
upgradesButton = addButton(object : LargeRectangleButtonPanel<S>( upgradesButton = addButton(object : LargeRectangleButtonPanel<S>(
screen, this@DeviceControls, screen, this@DeviceControls,
skinElement = Widgets18.UPGRADES icon = Widgets18.UPGRADES
) { ) {
init { init {
tooltips.add(TranslatableComponent("otm.gui.upgrades")) tooltips.add(TranslatableComponent("otm.gui.upgrades"))
@ -473,7 +473,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
} }
if (itemConfig != null) { 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 { init {
tooltips.add(TranslatableComponent("otm.gui.sides.item_config")) tooltips.add(TranslatableComponent("otm.gui.sides.item_config"))
} }
@ -492,7 +492,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
} }
if (energyConfig != null) { 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 { init {
tooltips.add(TranslatableComponent("otm.gui.sides.energy_config")) tooltips.add(TranslatableComponent("otm.gui.sides.energy_config"))
} }
@ -511,7 +511,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
} }
if (fluidConfig != null) { 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 { init {
tooltips.add(TranslatableComponent("otm.gui.sides.fluid_config")) 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() { override fun tickInner() {
super.tickInner() super.tickInner()
x = (parent?.width ?: 0f) + 3f 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 { 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.GuiGraphics
import net.minecraft.client.gui.screens.Screen 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.IGUIRenderable
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.menu.MatteryMenu
open class LargeRectangleButtonPanel<out S : Screen>( open class LargeRectangleButtonPanel<out S : Screen>(
screen: S, screen: S,
@ -15,8 +17,8 @@ open class LargeRectangleButtonPanel<out S : Screen>(
width: Float = SIZE, width: Float = SIZE,
height: Float = SIZE, height: Float = SIZE,
onPress: ((clickButton: Int) -> Unit)? = null, onPress: ((clickButton: Int) -> Unit)? = null,
var skinElement: IGUIRenderable? = null, var icon: IGUIRenderable? = null,
var skinElementWinding: UVWindingOrder? = null, var iconWinding: UVWindingOrder? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, onPress) { ) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, onPress) {
final override val IDLE = Widgets18.BUTTON_IDLE final override val IDLE = Widgets18.BUTTON_IDLE
final override val HOVERED = Widgets18.BUTTON_HOVERED 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) { override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
super.innerRender(graphics, mouseX, mouseY, partialTick) super.innerRender(graphics, mouseX, mouseY, partialTick)
if (skinElementWinding != null) { if (iconWinding != null) {
skinElement?.render(graphics, width = width, height = height, winding = skinElementWinding!!) icon?.render(graphics, width = width, height = height, winding = iconWinding!!)
} else { } else {
skinElement?.render(graphics, width = width, height = height) icon?.render(graphics, width = width, height = height)
} }
} }
companion object { companion object {
const val SIZE = 18f 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.dockResize = DockResizeMode.NONE
outputs.dockMargin = DockProperty(bottom = 3f) outputs.dockMargin = DockProperty(bottom = 3f)
object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, inputs, skinElement = STORE_1) { object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, inputs, icon = STORE_1) {
init { init {
dockRight = 3f dockRight = 3f
tooltips.add(TranslatableComponent("otm.gui.experience.store", 1)) tooltips.add(TranslatableComponent("otm.gui.experience.store", 1))
@ -108,7 +108,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
set(value) {} set(value) {}
} }
object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, inputs, skinElement = STORE_10) { object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, inputs, icon = STORE_10) {
init { init {
dockRight = 3f dockRight = 3f
tooltips.add(TranslatableComponent("otm.gui.experience.store", 10)) tooltips.add(TranslatableComponent("otm.gui.experience.store", 10))
@ -123,7 +123,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
set(value) {} set(value) {}
} }
object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, inputs, skinElement = STORE_ALL) { object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, inputs, icon = STORE_ALL) {
init { init {
dockRight = 3f dockRight = 3f
tooltips.add(TranslatableComponent("otm.gui.experience.store_all")) tooltips.add(TranslatableComponent("otm.gui.experience.store_all"))
@ -138,7 +138,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
set(value) {} set(value) {}
} }
object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, outputs, skinElement = DISPENSE_1) { object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, outputs, icon = DISPENSE_1) {
init { init {
dockRight = 3f dockRight = 3f
tooltips.add(TranslatableComponent("otm.gui.experience.dispense", 1)) tooltips.add(TranslatableComponent("otm.gui.experience.dispense", 1))
@ -153,7 +153,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
set(value) {} set(value) {}
} }
object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, outputs, skinElement = DISPENSE_10) { object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, outputs, icon = DISPENSE_10) {
init { init {
dockRight = 3f dockRight = 3f
tooltips.add(TranslatableComponent("otm.gui.experience.dispense", 10)) tooltips.add(TranslatableComponent("otm.gui.experience.dispense", 10))
@ -168,7 +168,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
set(value) {} set(value) {}
} }
object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, outputs, skinElement = DISPENSE_ALL) { object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, outputs, icon = DISPENSE_ALL) {
init { init {
dockRight = 3f dockRight = 3f
tooltips.add(TranslatableComponent("otm.gui.experience.dispense_all")) 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) val customBar = HorizontalStripPanel(this, frame, height = 18f)
customBar.dock = Dock.TOP customBar.dock = Dock.TOP
object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, customBar, skinElement = STORE_CUSTOM) { object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, customBar, icon = STORE_CUSTOM) {
init { init {
tooltips.add(TranslatableComponent("otm.gui.experience.store", customDispense)) 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 { init {
tooltips.add(TranslatableComponent("otm.gui.experience.dispense", customDispense)) tooltips.add(TranslatableComponent("otm.gui.experience.dispense", customDispense))
} }
@ -241,7 +241,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
set(value) {} set(value) {}
} }
object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, customBar, skinElement = SET_EXACT) { object : LargeRectangleButtonPanel<EssenceStorageScreen>(this@EssenceStorageScreen, customBar, icon = SET_EXACT) {
init { init {
tooltips.add(TranslatableComponent("otm.gui.experience.set_exact", customDispense)) tooltips.add(TranslatableComponent("otm.gui.experience.set_exact", customDispense))
dock = Dock.RIGHT 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.ImmutableMap
import com.google.common.collect.ImmutableSet import com.google.common.collect.ImmutableSet
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet 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.ints.IntSet
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player 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 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.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.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.concatIterators
import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.flatMap 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.LinkedList
import java.util.stream.Stream import java.util.stream.Stream
class CombinedContainer(containers: Stream<Pair<Container, Iterator<Int>>>) : Container, IIterableContainer { class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IMatteryContainer {
constructor(vararg containers: Container) : this(containers.stream().map { it to (0 until it.containerSize).iterator() }) 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).iterator() }) constructor(containers: Collection<Container>) : this(containers.stream().map { it to (0 until it.containerSize) })
private val slots: List<ContainerSlot> private inner class Slot(override val slot: Int, val outer: IContainerSlot) : IContainerSlot by outer {
private val slotsMap: Map<Container, List<ContainerSlot>> 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 containers: Set<Container>
private val fullCoverage: List<Container> private val fullCoverage: List<Container>
private val notFullCoverage: Map<Container, List<ContainerSlot>> private val notFullCoverage: Map<Container, List<IContainerSlot>>
init { init {
val list = ImmutableList.Builder<ContainerSlot>() val list = ImmutableList.Builder<Slot>()
var i = 0 var i = 0
val validationMap = Reference2ObjectOpenHashMap<Container, IntSet>() val validationMap = Reference2ObjectOpenHashMap<Container, IntSet>()
val slotsMap = Reference2ObjectOpenHashMap<Container, ArrayList<ContainerSlot>>() val slotsMap = Reference2ObjectOpenHashMap<Container, ArrayList<IContainerSlot>>()
for ((container, slots) in containers) { for ((container, slots) in containers) {
val validator = validationMap.computeIfAbsent(container, Object2ObjectFunction { IntAVLTreeSet() }) val validator = validationMap.computeIfAbsent(container, Object2ObjectFunction { IntAVLTreeSet() })
@ -46,9 +60,8 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterator<Int>>>) : Co
for (slot in slots) { for (slot in slots) {
if (validator.add(slot)) { if (validator.add(slot)) {
i++ val slotObj = container.containerSlot(slot)
val slotObj = ContainerSlot(slot, container) list.add(Slot(i++, slotObj))
list.add(slotObj)
slotList.add(slotObj) slotList.add(slotObj)
} else { } else {
throw IllegalArgumentException("Duplicate mapping for $container at $i for slot $slot") 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 return true
} }
fun slotAt(index: Int): ContainerSlot { override fun getItem(slot: Int): ItemStack {
return slots[index]
}
override fun getItem(index: Int): ItemStack {
// do not violate contract of getItem not throwing exceptions when index is invalid // 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 { override fun removeItem(slot: Int, amount: Int): ItemStack {
val data = slots.getOrNull(index) ?: return ItemStack.EMPTY val data = slots.getOrNull(slot) ?: return ItemStack.EMPTY
return data.container.removeItem(data.slot, count) return data.outer.container.removeItem(data.outer.slot, amount)
} }
override fun removeItemNoUpdate(index: Int): ItemStack { override fun removeItemNoUpdate(slot: Int): ItemStack {
val data = slots.getOrNull(index) ?: return ItemStack.EMPTY val data = slots.getOrNull(slot) ?: return ItemStack.EMPTY
return data.container.removeItemNoUpdate(data.slot) return data.outer.container.removeItemNoUpdate(data.outer.slot)
} }
override fun setItem(index: Int, value: ItemStack) { override fun setItem(slot: Int, itemStack: ItemStack) {
slots.getOrNull(index)?.item = value slots.getOrNull(slot)?.item = itemStack
} }
override fun setChanged() { 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 { override fun stillValid(player: Player): Boolean {
for (container in containers) for (container in containers)
if (!container.stillValid(player)) if (!container.stillValid(player))
@ -168,33 +167,64 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterator<Int>>>) : Co
return slots.iterator() 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 { class Builder {
private var built = false private val values = ArrayList<Pair<Container, Iterable<Int>>>()
private val values = LinkedList<Pair<Container, Iterator<Int>>>()
fun add(container: Container): Builder { fun add(container: Container): Builder {
check(!built) { "Already built!" } values.add(container to container.slotRange)
values.add(container to (0 until container.containerSize).iterator())
return this return this
} }
fun add(container: Container, slots: Iterator<Int>): Builder { fun add(container: Container, slots: Iterator<Int>): Builder {
check(!built) { "Already built!" } values.add(container to IntArrayList(slots))
values.add(container to 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 return this
} }
fun add(container: Container, slots: Iterable<Int>): Builder { fun add(container: Container, slots: Iterable<Int>): Builder {
check(!built) { "Already built!" } values.add(container to slots)
values.add(container to slots.iterator())
return this return this
} }
fun build(): CombinedContainer { fun build(): CombinedContainer {
check(!built) { "Already built!" } return CombinedContainer(values.stream())
val value = CombinedContainer(values.stream()) }
values.clear() }
return value
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.minecraft.world.item.ItemStack
import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.IItemHandler
class ContainerHandler @JvmOverloads internal constructor( class ContainerHandler(
private val container: MatteryContainer, private val container: IMatteryContainer,
private val filter: HandlerFilter = HandlerFilter.Both, private val filter: HandlerFilter = HandlerFilter.Both,
) : IItemHandler { ) : IItemHandler {
override fun getSlots() = container.containerSize 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.IntAVLTreeSet
import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.ints.IntArrayList
import it.unimi.dsi.fastutil.ints.IntArraySet 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.ints.IntSet
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap 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.ItemStack
import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse
import net.minecraftforge.fluids.capability.IFluidHandler 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.ItemStackHashStrategy
import ru.dbotthepony.mc.otm.container.util.slotIterator import ru.dbotthepony.mc.otm.container.util.slotIterator
import ru.dbotthepony.mc.otm.core.addAll 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.isNotEmpty
import ru.dbotthepony.mc.otm.core.map
import ru.dbotthepony.mc.otm.core.util.ItemStackSorter import ru.dbotthepony.mc.otm.core.util.ItemStackSorter
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -27,13 +33,30 @@ inline operator fun Container.get(index: Int): ItemStack = getItem(index)
@Suppress("nothing_to_inline") @Suppress("nothing_to_inline")
inline operator fun IFluidHandler.get(index: Int) = getFluidInTank(index) inline operator fun IFluidHandler.get(index: Int) = getFluidInTank(index)
fun Container.addItem(stack: ItemStack, range: IntRange, simulate: Boolean = false): ItemStack { val Container.slotRange: IntIterable get() {
if (this is MatteryContainer) { return IntIterable {
return this.addItem(stack, range, simulate) val i = (0 until containerSize).iterator()
}
if (range.last >= containerSize || range.first < 0) object : IntIterator {
throw IllegalArgumentException("Invalid range: $range") 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) if (stack.isEmpty)
return stack return stack
@ -41,7 +64,11 @@ fun Container.addItem(stack: ItemStack, range: IntRange, simulate: Boolean = fal
val copy = stack.copy() 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)) { if (ItemStack.isSameItemSameTags(this[slot], copy)) {
val slotStack = this[slot] val slotStack = this[slot]
val slotLimit = maxStackSize.coerceAtMost(slotStack.maxStackSize) 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) { if (this[slot].isEmpty) {
val diff = copy.count.coerceAtMost(maxStackSize.coerceAtMost(copy.maxStackSize)) 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 return copy
} }
fun Container.addItem(stack: ItemStack, simulate: Boolean): ItemStack = addItem(stack, 0 until containerSize, simulate)
fun Container.vanishCursedItems() { fun Container.vanishCursedItems() {
for (slot in slotIterator()) { for (slot in slotIterator()) {
if (hasVanishingCurse(slot.item)) { if (hasVanishingCurse(slot.item)) {
@ -258,3 +287,31 @@ operator fun CraftingContainer.get(column: Int, row: Int, flop: Boolean): ItemSt
else else
get(column, row) 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 import java.util.function.Predicate
// passthrough all default methods to fix Kotlin bug related to implementation delegation not properly working on Java interfaces // 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 // https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods
interface IContainer : Container { interface IContainer : Container {
override fun getMaxStackSize(): Int { override fun getMaxStackSize(): Int {
@ -41,6 +42,16 @@ interface IContainer : Container {
return super.hasAnyMatching(predicate) 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 { companion object {
fun wrap(container: Container): IContainer { fun wrap(container: Container): IContainer {
if (container is 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.ListTag
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation 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.Player
import net.minecraft.world.entity.player.StackedContents import net.minecraft.world.entity.player.StackedContents
import net.minecraft.world.inventory.StackedContentsCompatible import net.minecraft.world.inventory.StackedContentsCompatible
@ -19,9 +19,7 @@ import net.minecraft.world.item.Item
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.registries.ForgeRegistries 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.IContainerSlot
import ru.dbotthepony.mc.otm.container.util.IIterableContainer
import ru.dbotthepony.mc.otm.core.addSorted import ru.dbotthepony.mc.otm.core.addSorted
import ru.dbotthepony.mc.otm.core.collect.any import ru.dbotthepony.mc.otm.core.collect.any
import ru.dbotthepony.mc.otm.core.collect.count 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.Stream
import java.util.stream.StreamSupport import java.util.stream.StreamSupport
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.Iterator
@Suppress("UNUSED") @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) constructor(size: Int) : this({}, size)
init { init {
@ -134,7 +131,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
return field return field
} }
fun setSlotFilter(slot: Int, filter: Item? = null) { final override fun setSlotFilter(slot: Int, filter: Item?): Boolean {
if (filters[slot] !== filter) { if (filters[slot] !== filter) {
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] final override fun getSlotFilter(slot: Int) = filters[slot]
fun hasSlotFilter(slot: Int) = filters[slot] !== null final override fun hasSlotFilter(slot: Int) = filters[slot] !== null
fun isSlotForbiddenForAutomation(slot: Int) = filters[slot] === Items.AIR 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) 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) { if (filters[slot] == null) {
return true return true
} else { } else {
@ -283,137 +282,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
} }
} }
fun hasEmptySlot(): Boolean { final override fun isEmpty(): 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 {
return nonEmptyIndices.isEmpty return nonEmptyIndices.isEmpty
} }
@ -484,17 +353,17 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
return old return old
} }
final override fun setItem(slot: Int, stack: ItemStack) { final override fun setItem(slot: Int, itemStack: ItemStack) {
if (slots[slot].isEmpty && stack.isEmpty || stack === slots[slot]) if (slots[slot].isEmpty && itemStack.isEmpty || itemStack === slots[slot])
return return
val old = slots[slot] val old = slots[slot]
slots[slot] = if (stack.isEmpty) ItemStack.EMPTY else stack slots[slot] = if (itemStack.isEmpty) ItemStack.EMPTY else itemStack
trackedSlots[slot] = if (stack.isEmpty) ItemStack.EMPTY else stack.copy() trackedSlots[slot] = if (itemStack.isEmpty) ItemStack.EMPTY else itemStack.copy()
updateEmptyFlag(slot) updateEmptyFlag(slot)
changeset++ changeset++
internalSetChanged(slot, stack, old) internalSetChanged(slot, itemStack, old)
} }
final override fun setChanged() { 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)) { if (!slots[slot].equals(trackedSlots[slot], false)) {
trackedSlots[slot] = slots[slot].copy() trackedSlots[slot] = slots[slot].copy()
updateEmptyFlag(slot) 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> { final override fun slotIterator(): kotlin.collections.Iterator<IContainerSlot> {
indicesReferenced = true 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> { final override fun slotIterator(nonEmpty: Boolean): kotlin.collections.Iterator<IContainerSlot> {
if (!nonEmpty) { if (!nonEmpty) {
return (0 until size).iterator().map { ContainerSlot(it, this) } return (0 until size).iterator().map { Slot(it) }
} else if (isEmpty) { } else if (isEmpty) {
return emptyIterator() return emptyIterator()
} else { } else {
indicesReferenced = true 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 { final override fun countItem(item: Item): Int {
return iterator().filter { it.item == item }.count().toInt() 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.Container
import net.minecraft.world.item.Item import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import ru.dbotthepony.mc.otm.container.IMatteryContainer
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.get import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.set 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.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty 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> { interface IContainerSlot : GetterSetter<ItemStack> {
val slot: Int val slot: Int
val container: Container val container: Container
@ -18,28 +24,23 @@ interface IContainerSlot : GetterSetter<ItemStack> {
operator fun component1() = slot operator fun component1() = slot
operator fun component2() = item operator fun component2() = item
fun getMaxStackSize(item: ItemStack = this.item): Int {
return container.maxStackSize
}
val isForbiddenForAutomation: Boolean get() { val isForbiddenForAutomation: Boolean get() {
val container = container return getFilter() === Items.AIR
if (container is MatteryContainer) {
return container.isSlotForbiddenForAutomation(slot)
} else {
return false
}
} }
val filter: Item? get() { fun getFilter(): Item?
val container = container
if (container is MatteryContainer) { /**
return container.getSlotFilter(slot) * @return whenever the filter was set. Returns false only if container can't be filtered.
} else { */
return null fun setFilter(filter: Item? = null): Boolean
}
}
val hasFilter: Boolean val hasFilter: Boolean
get() = filter != null get() = getFilter() != null
fun remove() { fun remove() {
container[slot] = ItemStack.EMPTY container[slot] = ItemStack.EMPTY
@ -54,13 +55,7 @@ interface IContainerSlot : GetterSetter<ItemStack> {
} }
fun setChanged() { fun setChanged() {
val container = container container.setChanged()
if (container is MatteryContainer) {
container.setChanged(slot)
} else {
container.setChanged()
}
} }
var item: ItemStack var item: ItemStack
@ -78,32 +73,28 @@ class ContainerSlot(override val slot: Int, override val container: Container) :
init { init {
require(slot in 0 until container.containerSize) { "Slot out of bounds: $slot (container: $container with size ${container.containerSize})" } 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> { fun Container.containerSlot(slot: Int): IContainerSlot {
/** if (this is IMatteryContainer) {
* Iterates over non-empty itemstacks of this container return containerSlot(slot)
*/ } else {
override fun iterator(): Iterator<ItemStack> { return ContainerSlot(slot, this)
return iterator(true)
} }
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) operator fun Container.iterator() = iterator(true)
fun Container.iterator(nonEmpty: Boolean): Iterator<ItemStack> { fun Container.iterator(nonEmpty: Boolean): Iterator<ItemStack> {
if (this is IIterableContainer) { if (this is IMatteryContainer) {
return iterator(nonEmpty) return iterator(nonEmpty)
} else if (nonEmpty) { } else if (nonEmpty) {
return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty } 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> { fun Container.slotIterator(nonEmpty: Boolean = true): Iterator<IContainerSlot> {
if (this is IIterableContainer) { if (this is IMatteryContainer) {
return slotIterator(nonEmpty) return slotIterator(nonEmpty)
} else if (nonEmpty) { } else if (nonEmpty) {
return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { ContainerSlot(it, this) } 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: fun <T> Comparator<T>.nullsFirst(): Comparator<T> {
// since Java generics are invariant, return Comparator.nullsFirst(this)
// 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<in T>.nullsLast(): Comparator<T?> { fun <T> Comparator<T>.nullsLast(): Comparator<T> {
return Comparator.nullsLast(this as Comparator<in T?>) return Comparator.nullsLast(this)
} }
class MappedComparator<T, O>(private val parent: Comparator<O>, private val mapper: (T) -> O) : Comparator<T> { 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) return collector.finisher().apply(instance)
} }
fun <T> Iterator<T>.toList(): MutableList<T> { fun <T> Iterator<T>.toList(expectedSize: Int = 16): MutableList<T> {
val result = ArrayList<T>() val result = ArrayList<T>(expectedSize)
result.addAll(this) result.addAll(this)
return result 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.storage.ItemStorageStack
import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.Widgets18
private fun Comparator<Item?>.stacks(): Comparator<ItemStack?> { private fun Comparator<Item>.stacks(): Comparator<ItemStack> {
return Comparator<ItemStack> { o1, o2 -> this@stacks.compare(o1.item, o2.item) }.nullsFirst() return Comparator { o1, o2 -> this@stacks.compare(o1.item, o2.item) }
} }
private fun Comparator<Item?>.storage(): Comparator<ItemStorageStack?> { private fun Comparator<Item>.storage(): Comparator<ItemStorageStack> {
return Comparator<ItemStorageStack> { o1, o2 -> this@storage.compare(o1.item, o2.item) }.nullsFirst() return Comparator { o1, o2 -> this@storage.compare(o1.item, o2.item) }
} }
object CreativeMenuItemComparator : Comparator<Item> { object CreativeMenuItemComparator : Comparator<Item> {
@ -156,7 +156,7 @@ object ItemStorageStackCountComparator : Comparator<ItemStorageStack> {
val NullsLast = nullsLast() 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 }), DEFAULT(CreativeMenuItemComparator, TranslatableComponent("otm.gui.sorting.default"), lazy { Widgets18.SORT_DEFAULT }),
NAME(ItemLocalizedNameComparator.thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.name"), lazy { Widgets18.SORT_ALPHABET }), 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 }), 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 icon: IGUIRenderable by icon
val title: MutableComponent get() = sTitle.copy() 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 }), 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 }), COUNT(ItemStackCountComparator.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 }), NAME(ItemStackNameComparator.thenComparing(ItemSorter.DEFAULT.stacks()), ItemSorter.NAME.title, lazy { Widgets18.SORT_ALPHABET }),
ID(ItemSorter.ID.stacks(), ItemSorter.ID.title, lazy { Widgets18.SORT_ID }), ID(ItemSorter.ID.stacks(), ItemSorter.ID.title, lazy { Widgets18.SORT_ID }),
MOD(ItemSorter.MOD.stacks(), ItemSorter.MOD.title, lazy { Widgets18.SORT_MODID }), 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 }), 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 icon: IGUIRenderable by icon
val title: MutableComponent get() = sTitle.copy() 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 }), 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 }), COUNT(ItemStorageStackCountComparator.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 }), NAME(ItemStorageStackNameComparator.thenComparing(ItemSorter.DEFAULT.storage()), ItemSorter.NAME.title, lazy { Widgets18.SORT_ALPHABET }),
ID(ItemSorter.ID.storage(), ItemSorter.ID.title, lazy { Widgets18.SORT_ID }), ID(ItemSorter.ID.storage(), ItemSorter.ID.title, lazy { Widgets18.SORT_ID }),
MOD(ItemSorter.MOD.storage(), ItemSorter.MOD.title, lazy { Widgets18.SORT_MODID }), 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 }), 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 icon: IGUIRenderable by icon
val title: MutableComponent get() = sTitle.copy() 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 net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability 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.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.getBarColor import ru.dbotthepony.mc.otm.capability.energy.getBarColor
import ru.dbotthepony.mc.otm.capability.energy.getBarWidth 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) { for (ply in event.server.playerList.players) {
val networkedChannels = ObjectOpenHashSet<UUID>(0) 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 val power = item.getCapability(MatteryCapability.ENERGY).orThrow() as Power
power.updateValues() power.updateValues()
if (power.values.isServer && networkedChannels.add(power.values.uuid)) { 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.cos.cosmeticArmorSlots
import ru.dbotthepony.mc.otm.compat.curios.curiosSlots import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
import ru.dbotthepony.mc.otm.container.IMatteryContainer
import ru.dbotthepony.mc.otm.container.ItemFilter 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.UpgradeContainer
import ru.dbotthepony.mc.otm.container.sort
import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet import ru.dbotthepony.mc.otm.core.collect.ConditionalEnumSet
import ru.dbotthepony.mc.otm.core.collect.ConditionalSet import ru.dbotthepony.mc.otm.core.collect.ConditionalSet
@ -105,8 +106,6 @@ abstract class MatteryMenu(
private val _playerInventorySlots = ArrayList<InventorySlot>() private val _playerInventorySlots = ArrayList<InventorySlot>()
private val _playerHotbarSlots = ArrayList<InventorySlot>() private val _playerHotbarSlots = ArrayList<InventorySlot>()
private val _playerMainSlots = ArrayList<InventorySlot>()
private val _playerExoSuitSlots = ArrayList<InventorySlot>()
private val _playerCombinedInventorySlots = ArrayList<InventorySlot>() private val _playerCombinedInventorySlots = ArrayList<InventorySlot>()
private val _exopackChargeSlots = ArrayList<MatterySlot>() private val _exopackChargeSlots = ArrayList<MatterySlot>()
@ -189,7 +188,7 @@ abstract class MatteryMenu(
fun intInput(allowSpectators: Boolean = false, handler: (Int) -> Unit) = PlayerInput(VarIntValueCodec, allowSpectators, handler) 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) val playerInventorySlots: List<InventorySlot> = Collections.unmodifiableList(_playerInventorySlots)
@ -199,17 +198,7 @@ abstract class MatteryMenu(
val playerHotbarSlots: List<InventorySlot> = Collections.unmodifiableList(_playerHotbarSlots) val playerHotbarSlots: List<InventorySlot> = Collections.unmodifiableList(_playerHotbarSlots)
/** /**
* inventory only * inventory + Exopack (in this order)
*/
val playerMainSlots: List<InventorySlot> = Collections.unmodifiableList(_playerMainSlots)
/**
* exosuit only
*/
val playerExoSuitSlots: List<InventorySlot> = Collections.unmodifiableList(_playerExoSuitSlots)
/**
* inventory + exosuit (in this order)
*/ */
val playerCombinedInventorySlots: List<InventorySlot> = Collections.unmodifiableList(_playerCombinedInventorySlots) val playerCombinedInventorySlots: List<InventorySlot> = Collections.unmodifiableList(_playerCombinedInventorySlots)
@ -217,6 +206,9 @@ abstract class MatteryMenu(
val exopackPowerLevel = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(mSynchronizer) val exopackPowerLevel = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(mSynchronizer)
var sortInventoryInput: PlayerInput<Nothing?>? = null
private set
var offhandSlot: InventorySlot? = null var offhandSlot: InventorySlot? = null
protected set protected set
@ -267,7 +259,7 @@ abstract class MatteryMenu(
return addFilterSlots(slots) 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 { override fun mayPlace(itemStack: ItemStack): Boolean {
return !isInventorySlotLocked(index) && super.mayPlace(itemStack) return !isInventorySlotLocked(index) && super.mayPlace(itemStack)
} }
@ -276,61 +268,30 @@ abstract class MatteryMenu(
return !isInventorySlotLocked(index) && super.mayPickup(player) 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 var chargeFlag: GetterSetter<Boolean>? = null
private set private set
init { init {
val mattery = player.matteryPlayer val mattery = player.matteryPlayer!!
if (mattery != null) { if (addFilter) {
if (container === inventory) { val mContainer = container as IMatteryContainer
if (slotIndex in mattery.regularSlotFilters.indices) {
filter = GetterSetter.of(
getter = { mattery.regularSlotFilters[slotIndex].value },
setter = nullableItemInput(true) { mattery.regularSlotFilters[slotIndex].value = it }::accept
)
}
if (slotIndex in mattery.regularSlotChargeFlag.indices) { filter = GetterSetter.of(
val input = booleanInput(true) { mattery.regularSlotChargeFlag[slotIndex].boolean = it } getter = { mContainer.getSlotFilter(slotIndex) },
setter = nullableItemInput(true) { mContainer.setSlotFilter(slotIndex, it) }::accept
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
} }
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) { 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, x: Int = 0, y: Int = 0) : this( constructor(type: net.minecraft.world.entity.EquipmentSlot) : this(inventory, 34 + type.ordinal, type)
inventory, 34 + type.ordinal, type, x, y
)
override fun mayPlace(itemStack: ItemStack): Boolean { override fun mayPlace(itemStack: ItemStack): Boolean {
return super.mayPlace(itemStack) && itemStack.canEquip(type, inventory.player) return super.mayPlace(itemStack) && itemStack.canEquip(type, inventory.player)
@ -351,6 +312,8 @@ abstract class MatteryMenu(
protected fun addInventorySlots(autoFrame: Boolean = !player.isSpectator) { protected fun addInventorySlots(autoFrame: Boolean = !player.isSpectator) {
check(_playerInventorySlots.isEmpty()) { "Already created inventory slots" } check(_playerInventorySlots.isEmpty()) { "Already created inventory slots" }
val mattery = player.matteryPlayer ?: return
autoCreateInventoryFrame = autoFrame autoCreateInventoryFrame = autoFrame
offhandSlot = object : InventorySlot(inventory, 40) { offhandSlot = object : InventorySlot(inventory, 40) {
@ -362,11 +325,12 @@ abstract class MatteryMenu(
mapQuickMoveToInventory(offhandSlot!!) mapQuickMoveToInventory(offhandSlot!!)
addSlot(offhandSlot!!) addSlot(offhandSlot!!)
for (i in 0 .. 35) { for (i in 0 until mattery.combinedInventory.containerSize) {
val slot = InventorySlot(inventory, i) if (i in Inventory.INVENTORY_SIZE .. player.inventory.containerSize) continue
val slot = InventorySlot(mattery.combinedInventory, i, true)
_playerInventorySlots.add(slot) _playerInventorySlots.add(slot)
_playerMainSlots.add(slot)
if (i <= 8) if (i <= 8)
_playerHotbarSlots.add(slot) _playerHotbarSlots.add(slot)
@ -378,21 +342,7 @@ abstract class MatteryMenu(
addSlot(slot) addSlot(slot)
} }
val mattery = player.matteryPlayer if (mattery.hasExopack) {
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)
}
_exopackChargeSlots.add(BatterySlot(mattery.exopackEnergy.parent, 0).also { mapQuickMoveToExternal(it); mapQuickMoveToInventory(it); addSlot(it) }) _exopackChargeSlots.add(BatterySlot(mattery.exopackEnergy.parent, 0).also { mapQuickMoveToExternal(it); mapQuickMoveToInventory(it); addSlot(it) })
for (i in 0 until mattery.exopackChargeSlots.containerSize) for (i in 0 until mattery.exopackChargeSlots.containerSize)
@ -400,6 +350,10 @@ abstract class MatteryMenu(
exopackPowerLevel.with(mattery.exopackEnergy) exopackPowerLevel.with(mattery.exopackEnergy)
} }
sortInventoryInput = PlayerInput(NullValueCodec) {
mattery.inventoryAndExopackNoHotbar.sort()
}
} }
private var broadcastOnce = false private var broadcastOnce = false
@ -471,7 +425,7 @@ abstract class MatteryMenu(
val input: PlayerInput<Item?> val input: PlayerInput<Item?>
val field: IField<Item?> val field: IField<Item?>
if (container is MatteryContainer) { if (container is IMatteryContainer) {
input = PlayerInput(ItemValueCodec.nullable, handler = { container.setSlotFilter(pSlot.slotIndex, it) }) input = PlayerInput(ItemValueCodec.nullable, handler = { container.setSlotFilter(pSlot.slotIndex, it) })
field = mSynchronizer.ComputedField(getter = { container.getSlotFilter(pSlot.slotIndex) }, ItemValueCodec.nullable) field = mSynchronizer.ComputedField(getter = { container.getSlotFilter(pSlot.slotIndex) }, ItemValueCodec.nullable)
} else { } 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.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.container.IMatteryContainer
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.runOnClient 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 { open fun canTakeItemForPickAll(): Boolean {
return true 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> { 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 id2tuple = Int2ObjectOpenHashMap<Tuple>()
val sortedView = ArrayList<Tuple>() val sortedView = ArrayList<Tuple>()
var sorter: Comparator<in ItemStorageStack> = ItemStorageStackSorter.DEFAULT var sorter: Comparator<ItemStorageStack> = ItemStorageStackSorter.DEFAULT
set(value) { set(value) {
if (field != value) { if (field != value) {
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 net.minecraft.world.entity.player.Player
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity 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.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.UserFilteredSlot import ru.dbotthepony.mc.otm.menu.UserFilteredSlot
@ -16,9 +18,12 @@ class CargoCrateMenu @JvmOverloads constructor(
tile: CargoCrateBlockEntity? = null tile: CargoCrateBlockEntity? = null
) : MatteryMenu(MMenus.CARGO_CRATE, p_38852_, inventory, tile) { ) : MatteryMenu(MMenus.CARGO_CRATE, p_38852_, inventory, tile) {
val storageSlots: List<UserFilteredSlot> val storageSlots: List<UserFilteredSlot>
private val trackedPlayerOpen = !inventory.player.isSpectator private val trackedPlayerOpen = !inventory.player.isSpectator
val sort = PlayerInput(NullValueCodec) {
tile?.container?.sort()
}
init { init {
val container = tile?.container ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY) val container = tile?.container ?: SimpleContainer(CargoCrateBlockEntity.CAPACITY)

View File

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

View File

@ -96,7 +96,7 @@ class ItemMonitorMenu(
if (ascendingSort) { if (ascendingSort) {
networkedItemView.sorter = sorting networkedItemView.sorter = sorting
} else { } 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