Matter panel sorting (with buttons), cleanup nbt extensions
This commit is contained in:
parent
f4146ffea9
commit
441f358e12
@ -638,6 +638,14 @@ private fun gui(provider: MatteryLanguageProvider) {
|
||||
|
||||
gui("side_mode.pull", "Pull")
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -643,6 +643,14 @@ private fun gui(provider: MatteryLanguageProvider) {
|
||||
|
||||
gui("side_mode.pull", "Автоматическое вытягивание")
|
||||
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", "Убывающая")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,30 +2,23 @@ package ru.dbotthepony.mc.otm.block.entity
|
||||
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.TooltipFlag
|
||||
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.state.BlockState
|
||||
import net.minecraftforge.common.capabilities.Capability
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
||||
import net.minecraftforge.common.util.LazyOptional
|
||||
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.container.HandlerFilter
|
||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||
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.TranslatableComponent
|
||||
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_) {
|
||||
val batteryContainer = MatteryContainer(::setChangedLight, 1).also(::addDroppableContainer)
|
||||
|
@ -2,7 +2,7 @@ package ru.dbotthepony.mc.otm.block.entity
|
||||
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
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.network.FieldSynchronizer
|
||||
|
||||
@ -30,7 +30,7 @@ abstract class AbstractRedstoneControl : INBTSerializable<CompoundTag?> {
|
||||
return
|
||||
}
|
||||
|
||||
redstoneSetting = nbt.getEnum(SETTING_KEY)
|
||||
redstoneSetting = nbt.mapString(SETTING_KEY, RedstoneSetting::valueOf, RedstoneSetting.LOW)
|
||||
redstoneSignal = nbt.getInt(SIGNAL_KEY)
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||
import ru.dbotthepony.mc.otm.registry.MItems
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistry
|
||||
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.matter.MatterManager
|
||||
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) {
|
||||
super.load(tag)
|
||||
mass = tag.mapIf("mass", Decimal::deserializeNBT) ?: BASELINE_MASS
|
||||
mass = tag.map("mass", Decimal::deserializeNBT) ?: BASELINE_MASS
|
||||
spinDirection = tag.getBoolean("spin_direction")
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
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.world.level.block.state.BlockState
|
||||
import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu
|
||||
@ -11,13 +13,17 @@ import java.util.HashMap
|
||||
import java.util.UUID
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
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.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.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.matter.IMatterGraphNode
|
||||
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) :
|
||||
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>()
|
||||
override val matterNode = Graph6Node<IMatterGraphNode>(this)
|
||||
|
||||
@ -39,9 +65,6 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
listeners.remove(menu)
|
||||
}
|
||||
|
||||
override val defaultDisplayName: Component
|
||||
get() = MACHINE_NAME
|
||||
|
||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
||||
return MatterPanelMenu(containerID, inventory, this)
|
||||
}
|
||||
@ -131,13 +154,21 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
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) {
|
||||
super.load(nbt)
|
||||
_tasks.clear()
|
||||
val list = nbt.getList("tasks", Tag.TAG_COMPOUND.toInt())
|
||||
val list = nbt.getCompoundList("tasks")
|
||||
|
||||
for (tag in list) {
|
||||
val task = ReplicationTask.deserializeNBT(tag)
|
||||
@ -146,6 +177,15 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
_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? {
|
||||
@ -184,8 +224,4 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
|
||||
_tasks.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val MACHINE_NAME = TranslatableComponent("block.overdrive_that_matters.matter_panel")
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
|
||||
import ru.dbotthepony.mc.otm.network.MatteryPacket
|
||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||
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.menu.storage.ItemMonitorMenu
|
||||
import ru.dbotthepony.mc.otm.storage.*
|
||||
@ -93,9 +93,9 @@ class ItemMonitorPlayerSettings : INBTSerializable<CompoundTag>, MatteryPacket {
|
||||
}
|
||||
|
||||
override fun deserializeNBT(nbt: CompoundTag) {
|
||||
ingredientPriority = nbt.getEnum(INGREDIENT_PRIORITY_KEY)
|
||||
resultTarget = nbt.getEnum(RESULT_TARGET_KEY)
|
||||
craftingAmount = nbt.getEnum(QUICK_CRAFT_AMOUNT_KEY)
|
||||
ingredientPriority = nbt.mapString(INGREDIENT_PRIORITY_KEY, IngredientPriority::valueOf, IngredientPriority.SYSTEM)
|
||||
resultTarget = nbt.mapString(RESULT_TARGET_KEY, ResultTarget::valueOf, ResultTarget.MIXED)
|
||||
craftingAmount = nbt.mapString(QUICK_CRAFT_AMOUNT_KEY, Amount::valueOf, Amount.STACK)
|
||||
}
|
||||
|
||||
fun read(buff: FriendlyByteBuf) {
|
||||
|
@ -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.getDecimal
|
||||
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.set
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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.isPositive
|
||||
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.storage.*
|
||||
import java.math.BigInteger
|
||||
@ -176,10 +176,7 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
|
||||
storedDifferentStacks = 0
|
||||
// nextID = 0L
|
||||
|
||||
nbt.ifHas("capacity") {
|
||||
driveCapacity = BigInteger(it)
|
||||
}
|
||||
|
||||
driveCapacity = nbt.map("capacity", ::BigInteger) ?: driveCapacity
|
||||
maxDifferentStacks = nbt.getInt("max_different_stacks")
|
||||
|
||||
for (entry in nbt.getList("items", Tag.TAG_COMPOUND.toInt())) {
|
||||
|
@ -16,12 +16,10 @@ import net.minecraftforge.common.util.LazyOptional
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
|
||||
import ru.dbotthepony.mc.otm.config.ConciseBalanceValues
|
||||
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.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.core.math.defineDecimal
|
||||
import ru.dbotthepony.mc.otm.core.nbt.map
|
||||
import ru.dbotthepony.mc.otm.core.nbt.mapIf
|
||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||
|
||||
sealed class BlockEnergyStorageImpl(
|
||||
@ -142,9 +140,9 @@ sealed class BlockEnergyStorageImpl(
|
||||
override fun deserializeNBT(nbt: CompoundTag?) {
|
||||
if (nbt == null) return
|
||||
batteryLevel = nbt.map(ENERGY_STORED_KEY, Decimal.Companion::deserializeNBT) ?: Decimal.ZERO
|
||||
maxBatteryLevelStorage = nbt.mapIf(ENERGY_STORED_MAX_KEY, Decimal.Companion::deserializeNBT)
|
||||
maxInputStorage = nbt.mapIf(MAX_INPUT_KEY, Decimal.Companion::deserializeNBT)
|
||||
maxOutputStorage = nbt.mapIf(MAX_OUTPUT_KEY, Decimal.Companion::deserializeNBT)
|
||||
maxBatteryLevelStorage = nbt.map(ENERGY_STORED_MAX_KEY, Decimal.Companion::deserializeNBT)
|
||||
maxInputStorage = nbt.map(MAX_INPUT_KEY, Decimal.Companion::deserializeNBT)
|
||||
maxOutputStorage = nbt.map(MAX_OUTPUT_KEY, Decimal.Companion::deserializeNBT)
|
||||
}
|
||||
|
||||
var resolver: LazyOptional<IMatteryEnergyStorage> = LazyOptional.of { this }
|
||||
|
@ -5,7 +5,7 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
|
||||
object WidgetLocation {
|
||||
val LARGE_BUTTON = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/large_button.png"), 72f, 18f)
|
||||
val STORAGE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/storage_controls.png"), 90f, 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 SLOT_BACKGROUNDS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/slot_backgrounds.png"), 72f, 72f)
|
||||
|
||||
|
@ -34,12 +34,14 @@ object Widgets18 {
|
||||
val BUTTON_DISABLED_STRETCHABLE = makeButton(buttonGrids)
|
||||
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 AZ = storageGrid.next()
|
||||
val COUNT = storageGrid.next()
|
||||
val COLON = storageGrid.next()
|
||||
val C = storageGrid.next()
|
||||
val ARROW_UP = storageGrid.next()
|
||||
val SORT_DEFAULT = storageGrid.next()
|
||||
val SORT_ALPHABET = 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)
|
||||
|
||||
|
@ -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.IReplicationTask
|
||||
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.panels.*
|
||||
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.slot.AbstractSlotPanel
|
||||
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.core.TranslatableComponent
|
||||
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.ReplicationRequestPacket
|
||||
import ru.dbotthepony.mc.otm.network.MenuNetworkChannel
|
||||
@ -28,26 +33,50 @@ class MatterPanelScreen(
|
||||
inventory: Inventory,
|
||||
title: Component
|
||||
) : MatteryScreen<MatterPanelMenu>(menu, inventory, title) {
|
||||
override fun makeMainFrame(): FramePanel<out MatteryScreen<*>> {
|
||||
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
|
||||
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 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, {
|
||||
if (isPatternView) {
|
||||
integerDivisionDown(menu.patterns.size, GRID_WIDTH)
|
||||
scrollPatterns = integerDivisionDown(menu.patterns.size, GRID_WIDTH)
|
||||
scrollPatterns
|
||||
} else {
|
||||
integerDivisionDown(menu.tasks.size, GRID_WIDTH)
|
||||
scrollTasks = integerDivisionDown(menu.tasks.size, GRID_WIDTH)
|
||||
scrollTasks
|
||||
}
|
||||
}, { _, _, _ -> })
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
@ -133,6 +162,8 @@ class MatterPanelScreen(
|
||||
private fun openTask(task: IReplicationTask<*>) {
|
||||
val frame = FramePanel.padded(this, null, 170f, 20f, TranslatableComponent("otm.container.matter_panel.task"))
|
||||
|
||||
frame.closeOnEscape = true
|
||||
|
||||
object : AbstractSlotPanel<MatterPanelScreen>(this@MatterPanelScreen, frame) {
|
||||
init {
|
||||
dock = Dock.LEFT
|
||||
@ -175,6 +206,8 @@ class MatterPanelScreen(
|
||||
private fun openPattern(pattern: IPatternState) {
|
||||
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 rowInput = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)
|
||||
val rowBottom = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)
|
||||
|
@ -345,11 +345,8 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
|
||||
|
||||
fun flash() {
|
||||
isFlashing = true
|
||||
|
||||
if (parent == null) {
|
||||
popup()
|
||||
}
|
||||
}
|
||||
|
||||
val isFlashFrame: Boolean
|
||||
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 {
|
||||
if (!isVisible() || !acceptMouseInput) return false
|
||||
|
||||
if (flashAnyBlocker()) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (flashAnyBlocker()) return true
|
||||
if (grabMouseInput) return mouseClickedInner(x, y, button)
|
||||
|
||||
for (child in visibleChildrenInternal) {
|
||||
@ -1366,9 +1359,11 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
|
||||
if (!isVisible() || !acceptMouseInput) return false
|
||||
|
||||
if (isGrabbingMouseInput() || withinBounds(x, y)) {
|
||||
if (acceptMouseInput && parent == null) popup()
|
||||
popup()
|
||||
return mouseClicked(x, y, button)
|
||||
} else if (withinExtendedBounds(x, y)) {
|
||||
popup(false)
|
||||
|
||||
for (child in visibleChildrenInternal) {
|
||||
if (child.mouseClickedChecked(x, y, button)) {
|
||||
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 {
|
||||
if (!isVisible() || !acceptMouseInput) return false
|
||||
|
||||
if (flashAnyBlocker(false)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (flashAnyBlocker(false)) return true
|
||||
if (grabMouseInput) return mouseReleasedInner(x, y, button)
|
||||
|
||||
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 {
|
||||
if (!isVisible() || !acceptMouseInput) return false
|
||||
|
||||
if (flashAnyBlocker(false)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (flashAnyBlocker(false)) return true
|
||||
if (grabMouseInput) return mouseDraggedInner(x, y, button, xDelta, yDelta)
|
||||
|
||||
for (child in visibleChildrenInternal) {
|
||||
@ -1472,7 +1459,6 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
protected open fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
|
||||
return true
|
||||
}
|
||||
@ -1537,6 +1523,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
if (focusedAsParent) return keyPressedInternal(key, scancode, mods)
|
||||
return false
|
||||
}
|
||||
|
||||
@ -1560,6 +1547,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
if (focusedAsParent) return keyReleasedInternal(key, scancode, mods)
|
||||
return false
|
||||
}
|
||||
|
||||
@ -1641,14 +1629,18 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
|
||||
isRemoved = true
|
||||
}
|
||||
|
||||
fun popup() {
|
||||
fun popup(focus: Boolean = true) {
|
||||
if (isRemoved) {
|
||||
return
|
||||
}
|
||||
|
||||
if (screen is MatteryScreen<*>) {
|
||||
if (screen is MatteryScreen<*> && parent == null) {
|
||||
screen.popup(this as EditablePanel<MatteryScreen<*>>)
|
||||
}
|
||||
|
||||
if (focus && !isEverFocused()) {
|
||||
requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
fun asGrid(): FlexGridPanel<*> {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.panels
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.gui.narration.NarratableEntry
|
||||
@ -133,6 +134,8 @@ open class FramePanel<out S : Screen>(
|
||||
|
||||
protected var dragging = false
|
||||
|
||||
var closeOnEscape = false
|
||||
|
||||
init {
|
||||
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) {
|
||||
RECTANGLE.render(stack, width = width, height = height)
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.ChatFormatting
|
||||
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.render.AbstractMatterySprite
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
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.menu.input.IPlayerInputWithFeedback
|
||||
|
||||
@ -17,9 +20,11 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
|
||||
width: Float,
|
||||
height: Float,
|
||||
val prop: GetterSetter<Boolean>,
|
||||
val skinElementActive: AbstractMatterySprite? = null,
|
||||
val skinElementInactive: AbstractMatterySprite? = null,
|
||||
var skinElementActive: AbstractMatterySprite? = null,
|
||||
var skinElementInactive: AbstractMatterySprite? = null,
|
||||
val onChange: ((newValue: Boolean) -> Unit)? = null,
|
||||
var tooltipActive: Component? = null,
|
||||
var tooltipInactive: Component? = null,
|
||||
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) {
|
||||
override fun onClick(mouseButton: Int) {
|
||||
val newValue = !prop.value
|
||||
@ -27,6 +32,39 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
|
||||
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
|
||||
get() {
|
||||
if (prop is IPlayerInputWithFeedback<Boolean>) {
|
||||
@ -46,4 +84,8 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
|
||||
skinElementInactive?.render(stack, width = width, height = height)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val SPACE = TextComponent("")
|
||||
}
|
||||
}
|
||||
|
@ -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.capability.FlowDirection
|
||||
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.screen.MatteryScreen
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
@ -192,29 +191,42 @@ private fun <S : MatteryScreen<*>> makeEnergyConfigPanel(
|
||||
return frame
|
||||
}
|
||||
|
||||
fun <S : MatteryScreen<*>> makeDeviceControls(
|
||||
class DeviceControls<out S : MatteryScreen<*>>(
|
||||
screen: S,
|
||||
parent: FramePanel<S>,
|
||||
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null,
|
||||
itemConfig: ItemHandlerPlayerInput? = null,
|
||||
energyConfig: EnergyPlayerInput? = null,
|
||||
): EditablePanel<S> {
|
||||
val panel = object : EditablePanel<S>(screen, parent, width = LargeEnumRectangleButtonPanel.SIZE, height = 0f, x = parent.width + 3f) {
|
||||
override fun tickInner() {
|
||||
super.tickInner()
|
||||
x = parent.width + 3f
|
||||
y = 0f
|
||||
}
|
||||
extra: Iterable<EditablePanel<S>> = listOf(),
|
||||
val redstoneConfig: IPlayerInputWithFeedback<RedstoneSetting>? = null,
|
||||
val itemConfig: ItemHandlerPlayerInput? = null,
|
||||
val energyConfig: EnergyPlayerInput? = null,
|
||||
) : EditablePanel<S>(screen, parent, x = parent.width + 3f, height = 0f, width = 0f) {
|
||||
val itemConfigButton: LargeRectangleButtonPanel<S>?
|
||||
val energyConfigButton: LargeRectangleButtonPanel<S>?
|
||||
val redstoneControlsButton: LargeEnumRectangleButtonPanel<S, RedstoneSetting>?
|
||||
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) {
|
||||
y += makeRedstoneSettingButton(screen, panel, y = y, control = redstone).height + 2f
|
||||
if (redstoneConfig != null) {
|
||||
redstoneControlsButton = addButton(makeRedstoneSettingButton(screen, this, control = redstoneConfig))
|
||||
} else {
|
||||
redstoneControlsButton = 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 {
|
||||
tooltip = TranslatableComponent("otm.gui.sides.item_config")
|
||||
}
|
||||
@ -227,11 +239,13 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
|
||||
frame.y = absoluteY + height + 8f
|
||||
}
|
||||
}
|
||||
}.height + 2f
|
||||
})
|
||||
} else {
|
||||
itemConfigButton = 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 {
|
||||
tooltip = TranslatableComponent("otm.gui.sides.energy_config")
|
||||
}
|
||||
@ -244,9 +258,26 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
|
||||
frame.y = absoluteY + height + 8f
|
||||
}
|
||||
}
|
||||
}.height + 2f
|
||||
})
|
||||
} else {
|
||||
energyConfigButton = null
|
||||
}
|
||||
}
|
||||
|
||||
panel.height = (y - 2f).coerceAtLeast(0f)
|
||||
return panel
|
||||
override fun tickInner() {
|
||||
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)
|
||||
}
|
||||
|
@ -158,12 +158,14 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
|
||||
}
|
||||
|
||||
val listing = ArrayList<Component>()
|
||||
val tooltipList = tooltipList
|
||||
val tooltip = tooltip
|
||||
|
||||
if (tooltipList != null) {
|
||||
listing.addAll(tooltipList!!)
|
||||
listing.addAll(tooltipList)
|
||||
listing.add(SPACE)
|
||||
} else if (tooltip != null) {
|
||||
listing.add(tooltip!!)
|
||||
listing.add(tooltip)
|
||||
listing.add(SPACE)
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.panels.button
|
||||
|
||||
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.render.AbstractMatterySprite
|
||||
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
||||
@ -19,7 +20,9 @@ open class LargeBooleanRectangleButtonPanel<out S : Screen>(
|
||||
skinElementActive: AbstractMatterySprite? = null,
|
||||
skinElementInactive: AbstractMatterySprite? = 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 HOVERED = Widgets18.BUTTON_HOVERED
|
||||
final override val PRESSED = Widgets18.BUTTON_PRESSED
|
||||
|
@ -11,7 +11,7 @@ import net.minecraftforge.common.util.INBTSerializable
|
||||
import net.minecraftforge.network.NetworkEvent
|
||||
import org.apache.logging.log4j.LogManager
|
||||
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.network.MatteryPacket
|
||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||
@ -266,7 +266,7 @@ class ItemFilter(
|
||||
if (nbt == null)
|
||||
return
|
||||
|
||||
nbt.ifHas("items", ListTag::class.java) {
|
||||
nbt.map("items") { it: ListTag ->
|
||||
for ((i, value) in it.withIndex()) {
|
||||
if (value is CompoundTag) {
|
||||
filter[i] = ItemStack.of(value)
|
||||
|
@ -10,7 +10,7 @@ import kotlin.jvm.JvmOverloads
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
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 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)) {
|
||||
slots[i] = ItemStack.of(it[i] as CompoundTag)
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ import net.minecraftforge.registries.IForgeRegistry
|
||||
import ru.dbotthepony.mc.otm.core.math.Vector
|
||||
import ru.dbotthepony.mc.otm.core.util.readInt
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.lang.ref.Reference
|
||||
import java.math.BigInteger
|
||||
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)
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import net.minecraft.nbt.LongArrayTag
|
||||
import net.minecraft.nbt.LongTag
|
||||
import net.minecraft.nbt.NbtAccounter
|
||||
import net.minecraft.nbt.NbtUtils
|
||||
import net.minecraft.nbt.NumericTag
|
||||
import net.minecraft.nbt.ShortTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.nbt.Tag
|
||||
@ -58,57 +59,13 @@ inline fun <R, reified T : Tag?> CompoundTag.map(key: String, consumer: (T) -> R
|
||||
return null
|
||||
}
|
||||
|
||||
inline fun <R, reified T : Tag> CompoundTag.mapIf(key: String, consumer: (T) -> R): R? {
|
||||
val tag = get(key)
|
||||
|
||||
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 <T> CompoundTag.mapString(index: String, mapper: (String) -> T, orElse: T): T {
|
||||
val tag = this[index] as? StringTag ?: return orElse
|
||||
return mapper.invoke(tag.asString)
|
||||
}
|
||||
|
||||
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
|
||||
fun CompoundTag.getByteList(key: String): MutableList<ByteTag> = getList(key, Tag.TAG_BYTE.toInt()) as MutableList<ByteTag>
|
||||
@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)
|
||||
}
|
||||
|
||||
fun CompoundTag.getBoolean(index: String, orElse: Boolean): Boolean {
|
||||
return (this[index] as? NumericTag)?.asInt?.let { it > 0 } ?: orElse
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
@ -16,7 +16,7 @@ import net.minecraft.world.level.Level
|
||||
import net.minecraftforge.common.capabilities.Capability
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
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.stream.Stream
|
||||
|
||||
@ -99,11 +99,9 @@ class PatternStorageItem : Item {
|
||||
}
|
||||
|
||||
override val storedPatterns: Int get() {
|
||||
stack.tag?.ifHas("otm_patterns", ListTag::class.java) {
|
||||
return it.size
|
||||
}
|
||||
|
||||
return 0
|
||||
return stack.tag?.map("otm_patterns") { it: ListTag ->
|
||||
it.size
|
||||
} ?: 0
|
||||
}
|
||||
|
||||
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() {
|
||||
stack.tag?.ifHas("otm_patterns", ListTag::class.java) {
|
||||
return it.stream().map { PatternState.deserializeNBT(it) }.filter { it != null } as Stream<out IPatternState>
|
||||
}
|
||||
|
||||
return Stream.empty()
|
||||
return stack.tag?.map("otm_patterns") { it: ListTag ->
|
||||
it.stream().map { PatternState.deserializeNBT(it) }.filter { it != null } as Stream<out IPatternState>
|
||||
} ?: Stream.empty()
|
||||
}
|
||||
|
||||
override fun insertPattern(
|
||||
|
@ -21,7 +21,7 @@ import net.minecraftforge.event.entity.player.EntityItemPickupEvent
|
||||
import ru.dbotthepony.mc.otm.core.TextComponent
|
||||
import ru.dbotthepony.mc.otm.capability.drive.DrivePool
|
||||
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.util.*
|
||||
|
||||
@ -88,7 +88,7 @@ class PortableCondensationDriveItem(capacity: Int) :
|
||||
fun getFilterSettings(drive: ItemStack): ItemFilter {
|
||||
val filter = ItemFilter(MAX_FILTERS)
|
||||
filter.isWhitelist = true
|
||||
drive.tag?.ifHas(FILTER_PATH, CompoundTag::class.java, filter::deserializeNBT)
|
||||
drive.tag?.map(FILTER_PATH, filter::deserializeNBT)
|
||||
return filter
|
||||
}
|
||||
|
||||
|
@ -4,21 +4,26 @@ import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraftforge.network.NetworkEvent
|
||||
import net.minecraftforge.network.PacketDistributor
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.mc.otm.block.entity.matter.MatterPanelBlockEntity
|
||||
import ru.dbotthepony.mc.otm.capability.matter.*
|
||||
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.MatterNetworkGraph
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
import ru.dbotthepony.mc.otm.network.*
|
||||
import ru.dbotthepony.mc.otm.registry.MMenus
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Supplier
|
||||
import kotlin.Comparator
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class CancelTaskPacket(val id: UUID) : MatteryPacket {
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
@ -146,66 +151,70 @@ class MatterPanelMenu @JvmOverloads constructor(
|
||||
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 tasks = ArrayList<IReplicationTask<*>>()
|
||||
var changeset = 0
|
||||
|
||||
fun networkPatternsUpdated(patterns: Collection<IPatternState>) {
|
||||
changeset++
|
||||
|
||||
for (pattern in patterns) {
|
||||
val index = this.patterns.indexOfFirst(pattern::matchId)
|
||||
|
||||
if (index != -1) {
|
||||
this.patterns[index] = pattern
|
||||
} else {
|
||||
this.patterns.add(pattern)
|
||||
this.patterns.addSorted(pattern, actualComparator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun networkPatternsRemoved(patterns: Collection<IPatternState>) {
|
||||
changeset++
|
||||
|
||||
for (pattern in patterns) {
|
||||
this.patterns.remove(pattern)
|
||||
}
|
||||
}
|
||||
|
||||
fun networkTasksUpdated(tasks: Collection<IReplicationTask<*>>) {
|
||||
changeset++
|
||||
|
||||
for (task in tasks) {
|
||||
val index = this.tasks.indexOfFirst(task::matchId)
|
||||
|
||||
if (index != -1) {
|
||||
this.tasks[index] = task
|
||||
} else {
|
||||
this.tasks.add(task)
|
||||
this.tasks.addSorted(task, actualTaskComparator)
|
||||
}
|
||||
|
||||
updateWatcher?.accept(task)
|
||||
}
|
||||
}
|
||||
|
||||
fun networkTasksRemoved(tasks: Collection<IReplicationTask<*>>) {
|
||||
changeset++
|
||||
|
||||
for (task in tasks) {
|
||||
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
|
||||
fun requestReplication(ply: ServerPlayer, id: UUID, count: Int) {
|
||||
if (ply.isSpectator) {
|
||||
|
@ -688,6 +688,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
private val getter: () -> V,
|
||||
private val codec: IStreamCodec<V>,
|
||||
name: String = nextFieldName(),
|
||||
private val observer: (new: V) -> Unit = {}
|
||||
) : AbstractField<V>(name), IField<V> {
|
||||
private var remote: 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) {
|
||||
clientValue = codec.read(stream)
|
||||
val newValue = codec.read(stream)
|
||||
clientValue = newValue
|
||||
observer.invoke(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import net.minecraft.world.item.CreativeModeTab
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.event.CreativeModeTabEvent
|
||||
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.util.WriteOnce
|
||||
import ru.dbotthepony.mc.otm.registry.MItems.BATTERY_CREATIVE
|
||||
@ -16,6 +17,8 @@ object MCreativeTabs {
|
||||
private set
|
||||
|
||||
fun register(event: CreativeModeTabEvent.Register) {
|
||||
CreativeMenuComparator.invalidate()
|
||||
|
||||
MAIN = event.registerCreativeModeTab(ResourceLocation(OverdriveThatMatters.MOD_ID, "main")) {
|
||||
it.icon { ItemStack(BATTERY_CREATIVE, 1) }
|
||||
it.title(TranslatableComponent("itemGroup.otm"))
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 711 B After Width: | Height: | Size: 803 B |
Binary file not shown.
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user