Matter panel sorting (with buttons), cleanup nbt extensions

This commit is contained in:
DBotThePony 2023-03-06 00:20:59 +07:00
parent f4146ffea9
commit 441f358e12
Signed by: DBot
GPG Key ID: DCC23B5715498507
32 changed files with 534 additions and 221 deletions

View File

@ -638,6 +638,14 @@ private fun gui(provider: MatteryLanguageProvider) {
gui("side_mode.pull", "Pull") gui("side_mode.pull", "Pull")
gui("side_mode.push", "Push") gui("side_mode.push", "Push")
gui("sorting.default", "Default sorting")
gui("sorting.name", "Sort by name")
gui("sorting.id", "Sort by ID")
gui("sorting.modid", "Sort by Namespace (mod) ID")
gui("sorting.count", "Sort by amount")
gui("sorting.ascending", "Ascending")
gui("sorting.descending", "Descending")
} }
} }

View File

@ -643,6 +643,14 @@ private fun gui(provider: MatteryLanguageProvider) {
gui("side_mode.pull", "Автоматическое вытягивание") gui("side_mode.pull", "Автоматическое вытягивание")
gui("side_mode.push", "Автоматическое выталкивание") gui("side_mode.push", "Автоматическое выталкивание")
gui("sorting.default", "Сортировка по умолчанию")
gui("sorting.name", "Сортировка по имени")
gui("sorting.id", "Сортировка по ID")
gui("sorting.modid", "Сортировка по пространству имён (моду)")
gui("sorting.count", "Сортировка по количеству")
gui("sorting.ascending", "Возрастающая")
gui("sorting.descending", "Убывающая")
} }
} }

View File

@ -2,30 +2,23 @@ package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.item.BlockItem import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.level.BlockGetter import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.capability.* import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.BlockEnergyStorageImpl
import ru.dbotthepony.mc.otm.capability.energy.ItemEnergyStorageImpl import ru.dbotthepony.mc.otm.capability.energy.ItemEnergyStorageImpl
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nbt.ifHas
import ru.dbotthepony.mc.otm.core.ifPresentK import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
abstract class MatteryPoweredBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(p_155228_, p_155229_, p_155230_) { abstract class MatteryPoweredBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(p_155228_, p_155229_, p_155230_) {
val batteryContainer = MatteryContainer(::setChangedLight, 1).also(::addDroppableContainer) val batteryContainer = MatteryContainer(::setChangedLight, 1).also(::addDroppableContainer)

View File

@ -2,7 +2,7 @@ package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.core.nbt.getEnum import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.network.FieldSynchronizer import ru.dbotthepony.mc.otm.network.FieldSynchronizer
@ -30,7 +30,7 @@ abstract class AbstractRedstoneControl : INBTSerializable<CompoundTag?> {
return return
} }
redstoneSetting = nbt.getEnum(SETTING_KEY) redstoneSetting = nbt.mapString(SETTING_KEY, RedstoneSetting::valueOf, RedstoneSetting.LOW)
redstoneSignal = nbt.getInt(SIGNAL_KEY) redstoneSignal = nbt.getInt(SIGNAL_KEY)
} }

View File

@ -27,7 +27,7 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MItems import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRegistry import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.core.math.getSphericalBlockPositions import ru.dbotthepony.mc.otm.core.math.getSphericalBlockPositions
import ru.dbotthepony.mc.otm.core.nbt.mapIf import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.matter.MatterManager import ru.dbotthepony.mc.otm.matter.MatterManager
import ru.dbotthepony.mc.otm.triggers.BlackHoleTrigger import ru.dbotthepony.mc.otm.triggers.BlackHoleTrigger
@ -164,7 +164,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery
override fun load(tag: CompoundTag) { override fun load(tag: CompoundTag) {
super.load(tag) super.load(tag)
mass = tag.mapIf("mass", Decimal::deserializeNBT) ?: BASELINE_MASS mass = tag.map("mass", Decimal::deserializeNBT) ?: BASELINE_MASS
spinDirection = tag.getBoolean("spin_direction") spinDirection = tag.getBoolean("spin_direction")
} }

View File

@ -1,5 +1,7 @@
package ru.dbotthepony.mc.otm.block.entity.matter package ru.dbotthepony.mc.otm.block.entity.matter
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu
@ -11,13 +13,17 @@ import java.util.HashMap
import java.util.UUID import java.util.UUID
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import ru.dbotthepony.mc.otm.core.TranslatableComponent import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.matter.* import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.core.nbt.getBoolean
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.ItemSorter
import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
@ -28,6 +34,26 @@ import java.util.stream.Stream
class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryDeviceBlockEntity(MBlockEntities.MATTER_PANEL, p_155229_, p_155230_), IMatterGraphNode, IReplicationTaskProvider { MatteryDeviceBlockEntity(MBlockEntities.MATTER_PANEL, p_155229_, p_155230_), IMatterGraphNode, IReplicationTaskProvider {
class PlayerSettings(var sorter: ItemSorter = ItemSorter.DEFAULT, var ascending: Boolean = true) : INBTSerializable<CompoundTag> {
override fun serializeNBT(): CompoundTag {
return CompoundTag().also {
it["sorter"] = sorter.name
it["ascending"] = ascending
}
}
override fun deserializeNBT(nbt: CompoundTag) {
sorter = nbt.mapString("sorter", ItemSorter::valueOf, ItemSorter.DEFAULT)
ascending = nbt.getBoolean("ascending", true)
}
}
private val playerSettings = Object2ObjectOpenHashMap<UUID, PlayerSettings>()
fun getPlayerSettings(ply: Player): PlayerSettings {
return playerSettings.computeIfAbsent(ply.uuid, Object2ObjectFunction { PlayerSettings() })
}
private val listeners = ArrayList<MatterPanelMenu>() private val listeners = ArrayList<MatterPanelMenu>()
override val matterNode = Graph6Node<IMatterGraphNode>(this) override val matterNode = Graph6Node<IMatterGraphNode>(this)
@ -39,9 +65,6 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
listeners.remove(menu) listeners.remove(menu)
} }
override val defaultDisplayName: Component
get() = MACHINE_NAME
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return MatterPanelMenu(containerID, inventory, this) return MatterPanelMenu(containerID, inventory, this)
} }
@ -131,13 +154,21 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
list.add(task.serializeNBT()) list.add(task.serializeNBT())
} }
nbt.put("tasks", list) nbt["tasks"] = list
val settings = CompoundTag()
for ((uuid, value) in playerSettings) {
settings[uuid.toString()] = value.serializeNBT()
}
nbt["settings"] = settings
} }
override fun load(nbt: CompoundTag) { override fun load(nbt: CompoundTag) {
super.load(nbt) super.load(nbt)
_tasks.clear() _tasks.clear()
val list = nbt.getList("tasks", Tag.TAG_COMPOUND.toInt()) val list = nbt.getCompoundList("tasks")
for (tag in list) { for (tag in list) {
val task = ReplicationTask.deserializeNBT(tag) val task = ReplicationTask.deserializeNBT(tag)
@ -146,6 +177,15 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
_tasks[task.id] = task _tasks[task.id] = task
} }
} }
playerSettings.clear()
nbt.map("settings") { it: CompoundTag ->
for (k in it.allKeys) {
playerSettings.computeIfAbsent(UUID.fromString(k), Object2ObjectFunction { PlayerSettings() })
.deserializeNBT(it.getCompound(k))
}
}
} }
override fun getTask(id: UUID): ReplicationTask? { override fun getTask(id: UUID): ReplicationTask? {
@ -184,8 +224,4 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
_tasks.clear() _tasks.clear()
} }
companion object {
private val MACHINE_NAME = TranslatableComponent("block.overdrive_that_matters.matter_panel")
}
} }

View File

@ -35,7 +35,7 @@ import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
import ru.dbotthepony.mc.otm.network.MatteryPacket import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.container.set import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.nbt.getEnum import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
import ru.dbotthepony.mc.otm.storage.* import ru.dbotthepony.mc.otm.storage.*
@ -93,9 +93,9 @@ class ItemMonitorPlayerSettings : INBTSerializable<CompoundTag>, MatteryPacket {
} }
override fun deserializeNBT(nbt: CompoundTag) { override fun deserializeNBT(nbt: CompoundTag) {
ingredientPriority = nbt.getEnum(INGREDIENT_PRIORITY_KEY) ingredientPriority = nbt.mapString(INGREDIENT_PRIORITY_KEY, IngredientPriority::valueOf, IngredientPriority.SYSTEM)
resultTarget = nbt.getEnum(RESULT_TARGET_KEY) resultTarget = nbt.mapString(RESULT_TARGET_KEY, ResultTarget::valueOf, ResultTarget.MIXED)
craftingAmount = nbt.getEnum(QUICK_CRAFT_AMOUNT_KEY) craftingAmount = nbt.mapString(QUICK_CRAFT_AMOUNT_KEY, Amount::valueOf, Amount.STACK)
} }
fun read(buff: FriendlyByteBuf) { fun read(buff: FriendlyByteBuf) {

View File

@ -20,7 +20,6 @@ import ru.dbotthepony.mc.otm.core.math.BlockRotation
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.getDecimal import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.getByteArrayList import ru.dbotthepony.mc.otm.core.nbt.getByteArrayList
import ru.dbotthepony.mc.otm.core.nbt.ifHas
import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.menu.tech.EnergyCounterMenu import ru.dbotthepony.mc.otm.menu.tech.EnergyCounterMenu
@ -101,7 +100,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
passed = nbt.getDecimal(PASSED_ENERGY_KEY) passed = nbt.getDecimal(PASSED_ENERGY_KEY)
ioLimit = nbt.map(IO_LIMIT_KEY, Decimal.Companion::deserializeNBT) ioLimit = nbt.map(IO_LIMIT_KEY, Decimal.Companion::deserializeNBT)
nbt.ifHas(POWER_HISTORY_POINTER_KEY, IntTag::class.java) { nbt.map(POWER_HISTORY_POINTER_KEY) { it: IntTag ->
historyTick = it.asInt historyTick = it.asInt
} }

View File

@ -11,7 +11,7 @@ import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.BigInteger import ru.dbotthepony.mc.otm.core.math.BigInteger
import ru.dbotthepony.mc.otm.core.math.isPositive import ru.dbotthepony.mc.otm.core.math.isPositive
import ru.dbotthepony.mc.otm.core.math.serializeNBT import ru.dbotthepony.mc.otm.core.math.serializeNBT
import ru.dbotthepony.mc.otm.core.nbt.ifHas import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.storage.* import ru.dbotthepony.mc.otm.storage.*
import java.math.BigInteger import java.math.BigInteger
@ -176,10 +176,7 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
storedDifferentStacks = 0 storedDifferentStacks = 0
// nextID = 0L // nextID = 0L
nbt.ifHas("capacity") { driveCapacity = nbt.map("capacity", ::BigInteger) ?: driveCapacity
driveCapacity = BigInteger(it)
}
maxDifferentStacks = nbt.getInt("max_different_stacks") maxDifferentStacks = nbt.getInt("max_different_stacks")
for (entry in nbt.getList("items", Tag.TAG_COMPOUND.toInt())) { for (entry in nbt.getList("items", Tag.TAG_COMPOUND.toInt())) {

View File

@ -16,12 +16,10 @@ import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.config.ConciseBalanceValues import ru.dbotthepony.mc.otm.config.ConciseBalanceValues
import ru.dbotthepony.mc.otm.config.VerboseBalanceValues import ru.dbotthepony.mc.otm.config.VerboseBalanceValues
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.mapIf
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
sealed class BlockEnergyStorageImpl( sealed class BlockEnergyStorageImpl(
@ -142,9 +140,9 @@ sealed class BlockEnergyStorageImpl(
override fun deserializeNBT(nbt: CompoundTag?) { override fun deserializeNBT(nbt: CompoundTag?) {
if (nbt == null) return if (nbt == null) return
batteryLevel = nbt.map(ENERGY_STORED_KEY, Decimal.Companion::deserializeNBT) ?: Decimal.ZERO batteryLevel = nbt.map(ENERGY_STORED_KEY, Decimal.Companion::deserializeNBT) ?: Decimal.ZERO
maxBatteryLevelStorage = nbt.mapIf(ENERGY_STORED_MAX_KEY, Decimal.Companion::deserializeNBT) maxBatteryLevelStorage = nbt.map(ENERGY_STORED_MAX_KEY, Decimal.Companion::deserializeNBT)
maxInputStorage = nbt.mapIf(MAX_INPUT_KEY, Decimal.Companion::deserializeNBT) maxInputStorage = nbt.map(MAX_INPUT_KEY, Decimal.Companion::deserializeNBT)
maxOutputStorage = nbt.mapIf(MAX_OUTPUT_KEY, Decimal.Companion::deserializeNBT) maxOutputStorage = nbt.map(MAX_OUTPUT_KEY, Decimal.Companion::deserializeNBT)
} }
var resolver: LazyOptional<IMatteryEnergyStorage> = LazyOptional.of { this } var resolver: LazyOptional<IMatteryEnergyStorage> = LazyOptional.of { this }

View File

@ -5,7 +5,7 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters
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, 18f) val STORAGE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/storage_controls.png"), 90f, 36f)
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

@ -34,12 +34,14 @@ 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 = 1, columns = 5) private val storageGrid = WidgetLocation.STORAGE_CONTROLS.grid(rows = 2, columns = 5)
val ARROW_DOWN = storageGrid.next() val ARROW_DOWN = storageGrid.next()
val AZ = storageGrid.next() val ARROW_UP = storageGrid.next()
val COUNT = storageGrid.next() val SORT_DEFAULT = storageGrid.next()
val COLON = storageGrid.next() val SORT_ALPHABET = storageGrid.next()
val C = storageGrid.next() val SORT_COUNT = storageGrid.next()
val SORT_MODID = storageGrid.next()
val SORT_ID = storageGrid.next()
private val miscGrid = WidgetLocation.MISC_18.grid(4, 4) private val miscGrid = WidgetLocation.MISC_18.grid(4, 4)

View File

@ -8,15 +8,20 @@ import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.matter.IPatternState import ru.dbotthepony.mc.otm.capability.matter.IPatternState
import ru.dbotthepony.mc.otm.capability.matter.IReplicationTask import ru.dbotthepony.mc.otm.capability.matter.IReplicationTask
import ru.dbotthepony.mc.otm.client.render.WidgetLocation import ru.dbotthepony.mc.otm.client.render.WidgetLocation
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.* import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeBooleanRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeEnumRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.input.EditBoxPanel import ru.dbotthepony.mc.otm.client.screen.panels.input.EditBoxPanel
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.util.DiscreteScrollBarPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.core.util.ItemSorter
import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu
import ru.dbotthepony.mc.otm.menu.matter.ReplicationRequestPacket import ru.dbotthepony.mc.otm.menu.matter.ReplicationRequestPacket
import ru.dbotthepony.mc.otm.network.MenuNetworkChannel import ru.dbotthepony.mc.otm.network.MenuNetworkChannel
@ -28,26 +33,50 @@ class MatterPanelScreen(
inventory: Inventory, inventory: Inventory,
title: Component title: Component
) : MatteryScreen<MatterPanelMenu>(menu, inventory, title) { ) : MatteryScreen<MatterPanelMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel<out MatteryScreen<*>> { override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
var isPatternView = true var isPatternView = true
var scrollPatterns = 0
var scrollTasks = 0
val frame = FramePanel.padded(this, null, GRID_WIDTH * AbstractSlotPanel.SIZE + ScrollBarConstants.WIDTH + 4f, GRID_HEIGHT * AbstractSlotPanel.SIZE, title) val frame = FramePanel.padded(this, null, GRID_WIDTH * AbstractSlotPanel.SIZE + ScrollBarConstants.WIDTH + 4f, GRID_HEIGHT * AbstractSlotPanel.SIZE, title)
val controls = DeviceControls(this, frame)
LargeBooleanRectangleButtonPanel(this, controls,
prop = menu.isAscendingGS,
skinElementActive = Widgets18.ARROW_UP,
skinElementInactive = Widgets18.ARROW_DOWN,
tooltipActive = TranslatableComponent("otm.gui.sorting.ascending"),
tooltipInactive = TranslatableComponent("otm.gui.sorting.descending"),
).also {
controls.addButton(it)
}
LargeEnumRectangleButtonPanel(this, controls, enum = ItemSorter::class.java, prop = menu.sortingGS, defaultValue = ItemSorter.DEFAULT).also {
controls.addButton(it)
it.add(ItemSorter.DEFAULT, skinElement = Widgets18.SORT_DEFAULT, tooltip = ItemSorter.DEFAULT.title)
it.add(ItemSorter.NAME, skinElement = Widgets18.SORT_ALPHABET, tooltip = ItemSorter.NAME.title)
it.add(ItemSorter.ID, skinElement = Widgets18.SORT_ID, tooltip = ItemSorter.ID.title)
it.add(ItemSorter.MOD, skinElement = Widgets18.SORT_MODID, tooltip = ItemSorter.MOD.title)
}
val scrollBar = DiscreteScrollBarPanel(this, frame, { val scrollBar = DiscreteScrollBarPanel(this, frame, {
if (isPatternView) { if (isPatternView) {
integerDivisionDown(menu.patterns.size, GRID_WIDTH) scrollPatterns = integerDivisionDown(menu.patterns.size, GRID_WIDTH)
scrollPatterns
} else { } else {
integerDivisionDown(menu.tasks.size, GRID_WIDTH) scrollTasks = integerDivisionDown(menu.tasks.size, GRID_WIDTH)
scrollTasks
} }
}, { _, _, _ -> }) }, { _, _, _ -> })
scrollBar.dock = Dock.RIGHT scrollBar.dock = Dock.RIGHT
frame.Tab(onOpen = { isPatternView = true }, activeIcon = PATTERN_LIST_ACTIVE, inactiveIcon = PATTERN_LIST_INACTIVE).also { frame.Tab(onOpen = { isPatternView = true; scrollBar.scroll = scrollPatterns }, activeIcon = PATTERN_LIST_ACTIVE, inactiveIcon = PATTERN_LIST_INACTIVE).also {
it.tooltip = TranslatableComponent("otm.container.matter_panel.patterns") it.tooltip = TranslatableComponent("otm.container.matter_panel.patterns")
} }
frame.Tab(onOpen = { isPatternView = false }, activeIcon = TASK_LIST_ACTIVE, inactiveIcon = TASK_LIST_INACTIVE).also { frame.Tab(onOpen = { isPatternView = false; scrollBar.scroll = scrollTasks }, activeIcon = TASK_LIST_ACTIVE, inactiveIcon = TASK_LIST_INACTIVE).also {
it.tooltip = TranslatableComponent("otm.container.matter_panel.tasks") it.tooltip = TranslatableComponent("otm.container.matter_panel.tasks")
} }
@ -133,6 +162,8 @@ class MatterPanelScreen(
private fun openTask(task: IReplicationTask<*>) { private fun openTask(task: IReplicationTask<*>) {
val frame = FramePanel.padded(this, null, 170f, 20f, TranslatableComponent("otm.container.matter_panel.task")) val frame = FramePanel.padded(this, null, 170f, 20f, TranslatableComponent("otm.container.matter_panel.task"))
frame.closeOnEscape = true
object : AbstractSlotPanel<MatterPanelScreen>(this@MatterPanelScreen, frame) { object : AbstractSlotPanel<MatterPanelScreen>(this@MatterPanelScreen, frame) {
init { init {
dock = Dock.LEFT dock = Dock.LEFT
@ -175,6 +206,8 @@ class MatterPanelScreen(
private fun openPattern(pattern: IPatternState) { private fun openPattern(pattern: IPatternState) {
val frame = FramePanel.padded(this, null, 213f, (ButtonPanel.HEIGHT + 3f) * 4f, TranslatableComponent("otm.container.matter_panel.task")) val frame = FramePanel.padded(this, null, 213f, (ButtonPanel.HEIGHT + 3f) * 4f, TranslatableComponent("otm.container.matter_panel.task"))
frame.closeOnEscape = true
val rowTop = EditablePanel(this, frame, height = ButtonPanel.HEIGHT) val rowTop = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)
val rowInput = EditablePanel(this, frame, height = ButtonPanel.HEIGHT) val rowInput = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)
val rowBottom = EditablePanel(this, frame, height = ButtonPanel.HEIGHT) val rowBottom = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)

View File

@ -345,11 +345,8 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
fun flash() { fun flash() {
isFlashing = true isFlashing = true
if (parent == null) {
popup() popup()
} }
}
val isFlashFrame: Boolean val isFlashFrame: Boolean
get() = flashingSince != null && flashingSince!!.millis % 400L <= 200L get() = flashingSince != null && flashingSince!!.millis % 400L <= 200L
@ -1338,11 +1335,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
final override fun mouseClicked(x: Double, y: Double, button: Int): Boolean { final override fun mouseClicked(x: Double, y: Double, button: Int): Boolean {
if (!isVisible() || !acceptMouseInput) return false if (!isVisible() || !acceptMouseInput) return false
if (flashAnyBlocker()) return true
if (flashAnyBlocker()) {
return true
}
if (grabMouseInput) return mouseClickedInner(x, y, button) if (grabMouseInput) return mouseClickedInner(x, y, button)
for (child in visibleChildrenInternal) { for (child in visibleChildrenInternal) {
@ -1366,9 +1359,11 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
if (!isVisible() || !acceptMouseInput) return false if (!isVisible() || !acceptMouseInput) return false
if (isGrabbingMouseInput() || withinBounds(x, y)) { if (isGrabbingMouseInput() || withinBounds(x, y)) {
if (acceptMouseInput && parent == null) popup() popup()
return mouseClicked(x, y, button) return mouseClicked(x, y, button)
} else if (withinExtendedBounds(x, y)) { } else if (withinExtendedBounds(x, y)) {
popup(false)
for (child in visibleChildrenInternal) { for (child in visibleChildrenInternal) {
if (child.mouseClickedChecked(x, y, button)) { if (child.mouseClickedChecked(x, y, button)) {
killFocusForEverythingExcept(child) killFocusForEverythingExcept(child)
@ -1390,11 +1385,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
final override fun mouseReleased(x: Double, y: Double, button: Int): Boolean { final override fun mouseReleased(x: Double, y: Double, button: Int): Boolean {
if (!isVisible() || !acceptMouseInput) return false if (!isVisible() || !acceptMouseInput) return false
if (flashAnyBlocker(false)) return true
if (flashAnyBlocker(false)) {
return true
}
if (grabMouseInput) return mouseReleasedInner(x, y, button) if (grabMouseInput) return mouseReleasedInner(x, y, button)
for (child in visibleChildrenInternal) { for (child in visibleChildrenInternal) {
@ -1434,11 +1425,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
final override fun mouseDragged(x: Double, y: Double, button: Int, xDelta: Double, yDelta: Double): Boolean { final override fun mouseDragged(x: Double, y: Double, button: Int, xDelta: Double, yDelta: Double): Boolean {
if (!isVisible() || !acceptMouseInput) return false if (!isVisible() || !acceptMouseInput) return false
if (flashAnyBlocker(false)) return true
if (flashAnyBlocker(false)) {
return true
}
if (grabMouseInput) return mouseDraggedInner(x, y, button, xDelta, yDelta) if (grabMouseInput) return mouseDraggedInner(x, y, button, xDelta, yDelta)
for (child in visibleChildrenInternal) { for (child in visibleChildrenInternal) {
@ -1472,7 +1459,6 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
return false return false
} }
protected open fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { protected open fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
return true return true
} }
@ -1537,6 +1523,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
} }
} }
if (focusedAsParent) return keyPressedInternal(key, scancode, mods)
return false return false
} }
@ -1560,6 +1547,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
} }
} }
if (focusedAsParent) return keyReleasedInternal(key, scancode, mods)
return false return false
} }
@ -1641,14 +1629,18 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
isRemoved = true isRemoved = true
} }
fun popup() { fun popup(focus: Boolean = true) {
if (isRemoved) { if (isRemoved) {
return return
} }
if (screen is MatteryScreen<*>) { if (screen is MatteryScreen<*> && parent == null) {
screen.popup(this as EditablePanel<MatteryScreen<*>>) screen.popup(this as EditablePanel<MatteryScreen<*>>)
} }
if (focus && !isEverFocused()) {
requestFocus()
}
} }
fun asGrid(): FlexGridPanel<*> { fun asGrid(): FlexGridPanel<*> {

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.client.screen.panels package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.narration.NarratableEntry import net.minecraft.client.gui.narration.NarratableEntry
@ -133,6 +134,8 @@ open class FramePanel<out S : Screen>(
protected var dragging = false protected var dragging = false
var closeOnEscape = false
init { init {
setDockPadding(PADDING, if (title != null) PADDING_TOP else PADDING, PADDING, PADDING) setDockPadding(PADDING, if (title != null) PADDING_TOP else PADDING, PADDING, PADDING)
} }
@ -182,6 +185,15 @@ open class FramePanel<out S : Screen>(
} }
override fun keyPressedInternal(key: Int, scancode: Int, mods: Int): Boolean {
if (key == InputConstants.KEY_ESCAPE && closeOnEscape) {
remove()
return true
}
return super.keyPressedInternal(key, scancode, mods)
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) { override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
RECTANGLE.render(stack, width = width, height = height) RECTANGLE.render(stack, width = width, height = height)

View File

@ -1,11 +1,14 @@
package ru.dbotthepony.mc.otm.client.screen.panels.button package ru.dbotthepony.mc.otm.client.screen.panels.button
import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.GetterSetter import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.value import ru.dbotthepony.mc.otm.core.value
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
@ -17,9 +20,11 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
width: Float, width: Float,
height: Float, height: Float,
val prop: GetterSetter<Boolean>, val prop: GetterSetter<Boolean>,
val skinElementActive: AbstractMatterySprite? = null, var skinElementActive: AbstractMatterySprite? = null,
val skinElementInactive: AbstractMatterySprite? = null, var skinElementInactive: AbstractMatterySprite? = null,
val onChange: ((newValue: Boolean) -> Unit)? = null, val onChange: ((newValue: Boolean) -> Unit)? = null,
var tooltipActive: Component? = null,
var tooltipInactive: Component? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) { ) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) {
override fun onClick(mouseButton: Int) { override fun onClick(mouseButton: Int) {
val newValue = !prop.value val newValue = !prop.value
@ -27,6 +32,39 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
onChange?.invoke(newValue) onChange?.invoke(newValue)
} }
override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered) {
val tooltipActive = tooltipActive
val tooltipInactive = tooltipInactive
val tooltipList = tooltipList
val tooltip = tooltip
if (tooltipActive != null || tooltipInactive != null || tooltipList != null || tooltip != null) {
val tooltips = ArrayList<Component>(2)
if (tooltipList != null) {
tooltips.addAll(tooltipList)
tooltips.add(SPACE)
} else if (tooltip != null) {
tooltips.add(tooltip)
tooltips.add(SPACE)
}
if (tooltipActive != null) {
tooltips.add(tooltipActive.copy().withStyle(if (prop.get()) ChatFormatting.WHITE else ChatFormatting.GRAY))
}
if (tooltipInactive != null) {
tooltips.add(tooltipInactive.copy().withStyle(if (!prop.get()) ChatFormatting.WHITE else ChatFormatting.GRAY))
}
screen.renderComponentTooltip(stack, tooltips, mouseX.toInt(), mouseY.toInt(), font)
}
}
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
}
override var isDisabled: Boolean override var isDisabled: Boolean
get() { get() {
if (prop is IPlayerInputWithFeedback<Boolean>) { if (prop is IPlayerInputWithFeedback<Boolean>) {
@ -46,4 +84,8 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
skinElementInactive?.render(stack, width = width, height = height) skinElementInactive?.render(stack, width = width, height = height)
} }
} }
companion object {
private val SPACE = TextComponent("")
}
} }

View File

@ -5,7 +5,6 @@ import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
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.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
@ -192,29 +191,42 @@ private fun <S : MatteryScreen<*>> makeEnergyConfigPanel(
return frame return frame
} }
fun <S : MatteryScreen<*>> makeDeviceControls( class DeviceControls<out S : MatteryScreen<*>>(
screen: S, screen: S,
parent: FramePanel<S>, parent: FramePanel<S>,
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null, extra: Iterable<EditablePanel<S>> = listOf(),
itemConfig: ItemHandlerPlayerInput? = null, val redstoneConfig: IPlayerInputWithFeedback<RedstoneSetting>? = null,
energyConfig: EnergyPlayerInput? = null, val itemConfig: ItemHandlerPlayerInput? = null,
): EditablePanel<S> { val energyConfig: EnergyPlayerInput? = null,
val panel = object : EditablePanel<S>(screen, parent, width = LargeEnumRectangleButtonPanel.SIZE, height = 0f, x = parent.width + 3f) { ) : EditablePanel<S>(screen, parent, x = parent.width + 3f, height = 0f, width = 0f) {
override fun tickInner() { val itemConfigButton: LargeRectangleButtonPanel<S>?
super.tickInner() val energyConfigButton: LargeRectangleButtonPanel<S>?
x = parent.width + 3f val redstoneControlsButton: LargeEnumRectangleButtonPanel<S, RedstoneSetting>?
y = 0f private var nextY = 0f
}
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P {
button.parent = this
button.x = 0f
button.y = nextY
nextY += button.height + 2f
height = nextY - 2f
width = button.width.coerceAtLeast(width)
return button
} }
var y = 0f init {
for (button in extra) {
addButton(button)
}
if (redstone != null) { if (redstoneConfig != null) {
y += makeRedstoneSettingButton(screen, panel, y = y, control = redstone).height + 2f redstoneControlsButton = addButton(makeRedstoneSettingButton(screen, this, control = redstoneConfig))
} else {
redstoneControlsButton = null
} }
if (itemConfig != null) { if (itemConfig != null) {
y += object : LargeRectangleButtonPanel<S>(screen, panel, y = y, skinElement = Widgets18.ITEMS_CONFIGURATION) { itemConfigButton = addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls, skinElement = Widgets18.ITEMS_CONFIGURATION) {
init { init {
tooltip = TranslatableComponent("otm.gui.sides.item_config") tooltip = TranslatableComponent("otm.gui.sides.item_config")
} }
@ -227,11 +239,13 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
frame.y = absoluteY + height + 8f frame.y = absoluteY + height + 8f
} }
} }
}.height + 2f })
} else {
itemConfigButton = null
} }
if (energyConfig != null) { if (energyConfig != null) {
y += object : LargeRectangleButtonPanel<S>(screen, panel, y = y, skinElement = Widgets18.ENERGY_CONFIGURATION) { energyConfigButton = addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls, y = nextY, skinElement = Widgets18.ENERGY_CONFIGURATION) {
init { init {
tooltip = TranslatableComponent("otm.gui.sides.energy_config") tooltip = TranslatableComponent("otm.gui.sides.energy_config")
} }
@ -244,9 +258,26 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
frame.y = absoluteY + height + 8f frame.y = absoluteY + height + 8f
} }
} }
}.height + 2f })
} else {
energyConfigButton = null
}
} }
panel.height = (y - 2f).coerceAtLeast(0f) override fun tickInner() {
return panel super.tickInner()
x = (parent?.width ?: 0f) + 3f
y = 0f
}
}
fun <S : MatteryScreen<*>> makeDeviceControls(
screen: S,
parent: FramePanel<S>,
extra: Iterable<EditablePanel<S>> = listOf(),
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null,
itemConfig: ItemHandlerPlayerInput? = null,
energyConfig: EnergyPlayerInput? = null,
): DeviceControls<S> {
return DeviceControls(screen, parent, extra = extra, redstoneConfig = redstone, itemConfig = itemConfig, energyConfig = energyConfig)
} }

View File

@ -158,12 +158,14 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
} }
val listing = ArrayList<Component>() val listing = ArrayList<Component>()
val tooltipList = tooltipList
val tooltip = tooltip
if (tooltipList != null) { if (tooltipList != null) {
listing.addAll(tooltipList!!) listing.addAll(tooltipList)
listing.add(SPACE) listing.add(SPACE)
} else if (tooltip != null) { } else if (tooltip != null) {
listing.add(tooltip!!) listing.add(tooltip)
listing.add(SPACE) listing.add(SPACE)
} }

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.client.screen.panels.button package ru.dbotthepony.mc.otm.client.screen.panels.button
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.Widgets18
@ -19,7 +20,9 @@ open class LargeBooleanRectangleButtonPanel<out S : Screen>(
skinElementActive: AbstractMatterySprite? = null, skinElementActive: AbstractMatterySprite? = null,
skinElementInactive: AbstractMatterySprite? = null, skinElementInactive: AbstractMatterySprite? = null,
onChange: ((newValue: Boolean) -> Unit)? = null, onChange: ((newValue: Boolean) -> Unit)? = null,
) : BooleanRectangleButtonPanel<S>(screen, parent, x, y, width, height, prop, skinElementActive, skinElementInactive, onChange) { tooltipActive: Component? = null,
tooltipInactive: Component? = null,
) : BooleanRectangleButtonPanel<S>(screen, parent, x, y, width, height, prop, skinElementActive, skinElementInactive, onChange, tooltipActive, tooltipInactive) {
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
final override val PRESSED = Widgets18.BUTTON_PRESSED final override val PRESSED = Widgets18.BUTTON_PRESSED

View File

@ -11,7 +11,7 @@ import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.network.NetworkEvent import net.minecraftforge.network.NetworkEvent
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.nbt.ifHas import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.network.MatteryPacket import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
@ -266,7 +266,7 @@ class ItemFilter(
if (nbt == null) if (nbt == null)
return return
nbt.ifHas("items", ListTag::class.java) { nbt.map("items") { it: ListTag ->
for ((i, value) in it.withIndex()) { for ((i, value) in it.withIndex()) {
if (value is CompoundTag) { if (value is CompoundTag) {
filter[i] = ItemStack.of(value) filter[i] = ItemStack.of(value)

View File

@ -10,7 +10,7 @@ import kotlin.jvm.JvmOverloads
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.core.nbt.ifHas import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import java.util.* import java.util.*
@ -53,7 +53,7 @@ open class MatteryContainer(val watcher: Runnable, private val size: Int) : Cont
} }
// нам не интересен размер // нам не интересен размер
tag.ifHas("items", ListTag::class.java) { tag.map("items") { it: ListTag ->
for (i in 0 until it.size.coerceAtMost(size)) { for (i in 0 until it.size.coerceAtMost(size)) {
slots[i] = ItemStack.of(it[i] as CompoundTag) slots[i] = ItemStack.of(it[i] as CompoundTag)
} }

View File

@ -33,7 +33,6 @@ import net.minecraftforge.registries.IForgeRegistry
import ru.dbotthepony.mc.otm.core.math.Vector import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.util.readInt import ru.dbotthepony.mc.otm.core.util.readInt
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream
import java.lang.ref.Reference import java.lang.ref.Reference
import java.math.BigInteger import java.math.BigInteger
import java.util.Arrays import java.util.Arrays
@ -313,3 +312,72 @@ fun <T> Stream<T>.asIterable(): Iterable<T> {
} }
} }
} }
// Kotlin type safety:
// since Java generics are invariant,
// and can not distinguish between null and non-null type parameters
// we need to tell compiler that Comparator.nullsFirst actually has next signature:
// fun <T> Comparator.nullsFirst(original: Comparator<in T>): Comparator<in T?>
fun <T> Comparator<in T>.nullsFirst(): Comparator<T?> {
return Comparator.nullsFirst(this as Comparator<in T?>)
}
fun <T> Comparator<in T>.nullsLast(): Comparator<T?> {
return Comparator.nullsLast(this as Comparator<in T?>)
}
/**
* Returns applicable index to put [element] into [List] determined by [comparator], optionally specifying ranges as [fromIndex] and [toIndex]
*
* If [List] is not sorted, result of this function is undefined
*/
fun <E> List<E>.searchInsertionIndex(element: E, comparator: Comparator<E>, fromIndex: Int = 0, toIndex: Int = size): Int {
require(toIndex >= fromIndex) { "Invalid range: to $toIndex >= from $fromIndex" }
require(fromIndex >= 0) { "Invalid from index: $fromIndex" }
require(toIndex >= 0) { "Invalid to index: $toIndex" }
require(fromIndex <= size) { "Invalid from index: $fromIndex (list size $size)" }
require(toIndex <= size) { "Invalid to index: $toIndex (list size $size)" }
if (fromIndex == size || fromIndex == toIndex || comparator.compare(element, this[fromIndex]) <= 0) {
return fromIndex
}
if (toIndex - fromIndex <= 10) {
for (i in fromIndex + 1 until toIndex) {
val compare = comparator.compare(element, this[i])
if (compare <= 0) {
return i
}
}
return size
} else {
var lower = fromIndex
var upper = toIndex - 1
while (upper - lower >= 10) {
val middle = (upper + lower) / 2
val compare = comparator.compare(element, this[middle])
if (compare == 0) {
return middle
} else if (compare < 0) {
upper = middle
} else {
lower = middle
}
}
return searchInsertionIndex(element, comparator, lower, upper + 1)
}
}
/**
* Inserts [element] into [MutableList] at index determined by [comparator]
*
* If [MutableList] is not sorted, result of this function is undefined
*/
fun <E> MutableList<E>.addSorted(element: E, comparator: Comparator<E>) {
add(searchInsertionIndex(element, comparator), element)
}

View File

@ -15,6 +15,7 @@ import net.minecraft.nbt.LongArrayTag
import net.minecraft.nbt.LongTag import net.minecraft.nbt.LongTag
import net.minecraft.nbt.NbtAccounter import net.minecraft.nbt.NbtAccounter
import net.minecraft.nbt.NbtUtils import net.minecraft.nbt.NbtUtils
import net.minecraft.nbt.NumericTag
import net.minecraft.nbt.ShortTag import net.minecraft.nbt.ShortTag
import net.minecraft.nbt.StringTag import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
@ -58,57 +59,13 @@ inline fun <R, reified T : Tag?> CompoundTag.map(key: String, consumer: (T) -> R
return null return null
} }
inline fun <R, reified T : Tag> CompoundTag.mapIf(key: String, consumer: (T) -> R): R? { fun <T> CompoundTag.mapString(index: String, mapper: (String) -> T, orElse: T): T {
val tag = get(key) val tag = this[index] as? StringTag ?: return orElse
return mapper.invoke(tag.asString)
if (tag is T) {
return consumer(tag)
}
return null
}
inline fun <reified R : Enum<R>> CompoundTag.getEnum(key: String, ifNothing: () -> R = { R::class.java.enumConstants[0] }): R {
val tag = get(key)
if (tag is StringTag) {
val str = tag.asString
return R::class.java.enumConstants.first { it.name == str }
}
return ifNothing.invoke()
} }
fun CompoundTag.getItemStack(key: String): ItemStack = map(key, ItemStack::of) ?: ItemStack.EMPTY fun CompoundTag.getItemStack(key: String): ItemStack = map(key, ItemStack::of) ?: ItemStack.EMPTY
inline fun CompoundTag.ifHas(s: String, consumer: (Tag) -> Unit) {
val tag = get(s)
if (tag != null) {
consumer(tag)
}
}
inline fun CompoundTag.ifHas(s: String, type: Byte, consumer: (Tag) -> Unit) {
val tag = get(s)
if (tag != null && tag.id == type) {
consumer(tag)
}
}
inline fun <reified T : Tag> CompoundTag.ifHas(s: String, type: Class<T>, consumer: (T) -> Unit) {
val tag = get(s)
if (tag != null && tag::class.java === type) {
consumer(tag as T)
}
}
fun CompoundTag.getList(key: String): ListTag {
return this[key] as? ListTag ?: ListTag()
}
@Suppress("unchecked_cast") // type is checked inside getList @Suppress("unchecked_cast") // type is checked inside getList
fun CompoundTag.getByteList(key: String): MutableList<ByteTag> = getList(key, Tag.TAG_BYTE.toInt()) as MutableList<ByteTag> fun CompoundTag.getByteList(key: String): MutableList<ByteTag> = getList(key, Tag.TAG_BYTE.toInt()) as MutableList<ByteTag>
@Suppress("unchecked_cast") // type is checked inside getList @Suppress("unchecked_cast") // type is checked inside getList
@ -153,3 +110,7 @@ fun CompoundTag.getJson(key: String, sizeLimit: NbtAccounter = NbtAccounter.UNLI
return FastByteArrayInputStream(bytes).readJson(sizeLimit) return FastByteArrayInputStream(bytes).readJson(sizeLimit)
} }
fun CompoundTag.getBoolean(index: String, orElse: Boolean): Boolean {
return (this[index] as? NumericTag)?.asInt?.let { it > 0 } ?: orElse
}

View File

@ -0,0 +1,83 @@
package ru.dbotthepony.mc.otm.core.util
import it.unimi.dsi.fastutil.objects.Reference2IntFunction
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraftforge.common.CreativeModeTabRegistry
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.nullsFirst
import ru.dbotthepony.mc.otm.core.nullsLast
import ru.dbotthepony.mc.otm.core.registryName
object CreativeMenuComparator : Comparator<Item> {
override fun compare(o1: Item, o2: Item): Int {
rebuild()
return item2index.getInt(o1).compareTo(item2index.getInt(o2))
}
private val item2index = Reference2IntOpenHashMap<Item>()
init {
item2index.defaultReturnValue(Int.MAX_VALUE)
}
private fun rebuild() {
if (item2index.isEmpty()) {
var i = 0
for (tab in CreativeModeTabRegistry.getSortedCreativeModeTabs()) {
for (item in tab.displayItems) {
item2index.computeIfAbsent(item.item, Reference2IntFunction { i++ })
}
}
}
}
internal fun invalidate() {
item2index.clear()
}
val NullsFirst = nullsFirst()
val NullsLast = nullsLast()
}
object ItemLocalizedNameComparator : Comparator<Item> {
override fun compare(o1: Item, o2: Item): Int {
return o1.description.string.compareTo(o2.description.string)
}
val NullsFirst = nullsFirst()
val NullsLast = nullsLast()
}
object ItemModComparator : Comparator<Item> {
override fun compare(o1: Item, o2: Item): Int {
val a = o1.registryName?.namespace ?: return 0
val b = o2.registryName?.namespace ?: return 0
return a.compareTo(b)
}
val NullsFirst = nullsFirst()
val NullsLast = nullsLast()
}
object ItemIDComparator : Comparator<Item> {
override fun compare(o1: Item, o2: Item): Int {
val a = o1.registryName ?: return 0
val b = o2.registryName ?: return 0
return a.compareTo(b)
}
val NullsFirst = nullsFirst()
val NullsLast = nullsLast()
}
enum class ItemSorter(val comparator: Comparator<Item?>, private val sTitle: Component) {
DEFAULT(CreativeMenuComparator.NullsFirst, TranslatableComponent("otm.gui.sorting.default")),
NAME(ItemLocalizedNameComparator.NullsFirst.thenComparing(CreativeMenuComparator.NullsFirst), TranslatableComponent("otm.gui.sorting.name")),
ID(ItemIDComparator.NullsFirst.thenComparing(CreativeMenuComparator.NullsFirst), TranslatableComponent("otm.gui.sorting.id")),
MOD(ItemModComparator.NullsFirst.thenComparing(CreativeMenuComparator.NullsFirst), TranslatableComponent("otm.gui.sorting.modid"));
val title: Component get() = sTitle.copy()
}

View File

@ -16,7 +16,7 @@ import net.minecraft.world.level.Level
import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.Capability
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.capability.matter.* import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.core.nbt.ifHas import ru.dbotthepony.mc.otm.core.nbt.map
import java.util.* import java.util.*
import java.util.stream.Stream import java.util.stream.Stream
@ -99,11 +99,9 @@ class PatternStorageItem : Item {
} }
override val storedPatterns: Int get() { override val storedPatterns: Int get() {
stack.tag?.ifHas("otm_patterns", ListTag::class.java) { return stack.tag?.map("otm_patterns") { it: ListTag ->
return it.size it.size
} } ?: 0
return 0
} }
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> { override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
@ -111,11 +109,9 @@ class PatternStorageItem : Item {
} }
override val patterns: Stream<out IPatternState> get() { override val patterns: Stream<out IPatternState> get() {
stack.tag?.ifHas("otm_patterns", ListTag::class.java) { return stack.tag?.map("otm_patterns") { it: ListTag ->
return it.stream().map { PatternState.deserializeNBT(it) }.filter { it != null } as Stream<out IPatternState> it.stream().map { PatternState.deserializeNBT(it) }.filter { it != null } as Stream<out IPatternState>
} } ?: Stream.empty()
return Stream.empty()
} }
override fun insertPattern( override fun insertPattern(

View File

@ -21,7 +21,7 @@ import net.minecraftforge.event.entity.player.EntityItemPickupEvent
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.capability.drive.DrivePool import ru.dbotthepony.mc.otm.capability.drive.DrivePool
import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.nbt.ifHas import ru.dbotthepony.mc.otm.core.nbt.map
import java.math.BigInteger import java.math.BigInteger
import java.util.* import java.util.*
@ -88,7 +88,7 @@ class PortableCondensationDriveItem(capacity: Int) :
fun getFilterSettings(drive: ItemStack): ItemFilter { fun getFilterSettings(drive: ItemStack): ItemFilter {
val filter = ItemFilter(MAX_FILTERS) val filter = ItemFilter(MAX_FILTERS)
filter.isWhitelist = true filter.isWhitelist = true
drive.tag?.ifHas(FILTER_PATH, CompoundTag::class.java, filter::deserializeNBT) drive.tag?.map(FILTER_PATH, filter::deserializeNBT)
return filter return filter
} }

View File

@ -4,21 +4,26 @@ import net.minecraft.network.FriendlyByteBuf
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
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.inventory.Slot
import net.minecraftforge.network.NetworkEvent import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor import net.minecraftforge.network.PacketDistributor
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.block.entity.matter.MatterPanelBlockEntity import ru.dbotthepony.mc.otm.block.entity.matter.MatterPanelBlockEntity
import ru.dbotthepony.mc.otm.capability.matter.* import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.addSorted
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
import ru.dbotthepony.mc.otm.core.util.ItemSorter
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphListener import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphListener
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.network.* import ru.dbotthepony.mc.otm.network.*
import ru.dbotthepony.mc.otm.registry.MMenus import ru.dbotthepony.mc.otm.registry.MMenus
import java.util.* import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier import java.util.function.Supplier
import kotlin.Comparator
import kotlin.collections.ArrayList
class CancelTaskPacket(val id: UUID) : MatteryPacket { class CancelTaskPacket(val id: UUID) : MatteryPacket {
override fun write(buff: FriendlyByteBuf) { override fun write(buff: FriendlyByteBuf) {
@ -146,66 +151,70 @@ class MatterPanelMenu @JvmOverloads constructor(
sendNetwork(TasksChangePacket(false, listOf(task))) sendNetwork(TasksChangePacket(false, listOf(task)))
} }
// client code val sorting: ItemSorter by mSynchronizer.ComputedField(
getter = { tile?.getPlayerSettings(ply)?.sorter ?: ItemSorter.DEFAULT },
codec = EnumValueCodec(ItemSorter::class.java),
observer = {
patterns.sortWith(actualComparator)
tasks.sortWith(actualTaskComparator)
})
val isAscending: Boolean by mSynchronizer.ComputedField(
getter = { tile?.getPlayerSettings(ply)?.ascending ?: true },
codec = BooleanValueCodec,
observer = {
patterns.sortWith(actualComparator)
tasks.sortWith(actualTaskComparator)
})
val changeIsAscending = booleanInput(allowSpectators = true) { tile?.getPlayerSettings(ply)?.ascending = it }
val changeSorting = PlayerInput(EnumValueCodec(ItemSorter::class.java), allowSpectators = true) { tile?.getPlayerSettings(ply)?.sorter = it }
val sortingGS = GetterSetter.of(::sorting, changeSorting::input)
val isAscendingGS = GetterSetter.of(::isAscending, changeIsAscending::input)
private val actualComparator = Comparator<IPatternState> { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
private val actualTaskComparator = Comparator<IReplicationTask<*>> { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
val patterns = ArrayList<IPatternState>() val patterns = ArrayList<IPatternState>()
val tasks = ArrayList<IReplicationTask<*>>() val tasks = ArrayList<IReplicationTask<*>>()
var changeset = 0
fun networkPatternsUpdated(patterns: Collection<IPatternState>) { fun networkPatternsUpdated(patterns: Collection<IPatternState>) {
changeset++
for (pattern in patterns) { for (pattern in patterns) {
val index = this.patterns.indexOfFirst(pattern::matchId) val index = this.patterns.indexOfFirst(pattern::matchId)
if (index != -1) { if (index != -1) {
this.patterns[index] = pattern this.patterns[index] = pattern
} else { } else {
this.patterns.add(pattern) this.patterns.addSorted(pattern, actualComparator)
} }
} }
} }
fun networkPatternsRemoved(patterns: Collection<IPatternState>) { fun networkPatternsRemoved(patterns: Collection<IPatternState>) {
changeset++
for (pattern in patterns) { for (pattern in patterns) {
this.patterns.remove(pattern) this.patterns.remove(pattern)
} }
} }
fun networkTasksUpdated(tasks: Collection<IReplicationTask<*>>) { fun networkTasksUpdated(tasks: Collection<IReplicationTask<*>>) {
changeset++
for (task in tasks) { for (task in tasks) {
val index = this.tasks.indexOfFirst(task::matchId) val index = this.tasks.indexOfFirst(task::matchId)
if (index != -1) { if (index != -1) {
this.tasks[index] = task this.tasks[index] = task
} else { } else {
this.tasks.add(task) this.tasks.addSorted(task, actualTaskComparator)
} }
updateWatcher?.accept(task)
} }
} }
fun networkTasksRemoved(tasks: Collection<IReplicationTask<*>>) { fun networkTasksRemoved(tasks: Collection<IReplicationTask<*>>) {
changeset++
for (task in tasks) { for (task in tasks) {
this.tasks.remove(task) this.tasks.remove(task)
deleteWatcher?.accept(task)
} }
} }
private var deleteWatcher: Consumer<IReplicationTask<*>>? = null
private var updateWatcher: Consumer<IReplicationTask<*>>? = null
fun networkTaskWatcher(updateWatcher: Consumer<IReplicationTask<*>>, deleteWatcher: Consumer<IReplicationTask<*>>) {
this.deleteWatcher = deleteWatcher
this.updateWatcher = updateWatcher
}
// server code // server code
fun requestReplication(ply: ServerPlayer, id: UUID, count: Int) { fun requestReplication(ply: ServerPlayer, id: UUID, count: Int) {
if (ply.isSpectator) { if (ply.isSpectator) {

View File

@ -688,6 +688,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
private val getter: () -> V, private val getter: () -> V,
private val codec: IStreamCodec<V>, private val codec: IStreamCodec<V>,
name: String = nextFieldName(), name: String = nextFieldName(),
private val observer: (new: V) -> Unit = {}
) : AbstractField<V>(name), IField<V> { ) : AbstractField<V>(name), IField<V> {
private var remote: V? = null private var remote: V? = null
private var clientValue: V? = null private var clientValue: V? = null
@ -726,7 +727,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
} }
override fun read(stream: DataInputStream) { override fun read(stream: DataInputStream) {
clientValue = codec.read(stream) val newValue = codec.read(stream)
clientValue = newValue
observer.invoke(newValue)
} }
} }

View File

@ -5,6 +5,7 @@ import net.minecraft.world.item.CreativeModeTab
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.event.CreativeModeTabEvent import net.minecraftforge.event.CreativeModeTabEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.util.CreativeMenuComparator
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.util.WriteOnce import ru.dbotthepony.mc.otm.core.util.WriteOnce
import ru.dbotthepony.mc.otm.registry.MItems.BATTERY_CREATIVE import ru.dbotthepony.mc.otm.registry.MItems.BATTERY_CREATIVE
@ -16,6 +17,8 @@ object MCreativeTabs {
private set private set
fun register(event: CreativeModeTabEvent.Register) { fun register(event: CreativeModeTabEvent.Register) {
CreativeMenuComparator.invalidate()
MAIN = event.registerCreativeModeTab(ResourceLocation(OverdriveThatMatters.MOD_ID, "main")) { MAIN = event.registerCreativeModeTab(ResourceLocation(OverdriveThatMatters.MOD_ID, "main")) {
it.icon { ItemStack(BATTERY_CREATIVE, 1) } it.icon { ItemStack(BATTERY_CREATIVE, 1) }
it.title(TranslatableComponent("itemGroup.otm")) it.title(TranslatableComponent("itemGroup.otm"))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 B

After

Width:  |  Height:  |  Size: 803 B

View File

@ -0,0 +1,34 @@
package ru.dbotthepony.mc.otm.tests
import it.unimi.dsi.fastutil.ints.IntComparators
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import ru.dbotthepony.mc.otm.core.addSorted
import java.util.Random
object ComparatorTests {
@Test
@DisplayName("Comparator tests")
fun test() {
val sortedList = mutableListOf(1, 4, 6)
sortedList.addSorted(2, IntComparators.NATURAL_COMPARATOR)
sortedList.addSorted(3, IntComparators.NATURAL_COMPARATOR)
sortedList.addSorted(7, IntComparators.NATURAL_COMPARATOR)
sortedList.addSorted(-1, IntComparators.NATURAL_COMPARATOR)
assertEquals(mutableListOf(-1, 1, 2, 3, 4, 6, 7), sortedList)
val rand = Random()
val sorted2 = ArrayList<Int>()
for (i in 0 .. 100) {
sorted2.addSorted(rand.nextInt(-100, 100), IntComparators.NATURAL_COMPARATOR)
}
val sorted22 = ArrayList(sorted2)
sorted22.sort()
assertEquals(sorted22, sorted2)
}
}