diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt index 328d94b0f..3aab1fb17 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt @@ -613,6 +613,7 @@ private fun gui(provider: MatteryLanguageProvider) { gui("stored_amount", "Exact amount stored: %s") gui("sides.item_config", "Item Configuration") + gui("sides.energy_config", "Energy Configuration") gui("sides.top", "Top") gui("sides.bottom", "Bottom") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt index 4595ff051..d8446ebce 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt @@ -618,6 +618,7 @@ private fun gui(provider: MatteryLanguageProvider) { gui("stored_amount", "Точное количество в хранилище: %s шт.") gui("sides.item_config", "Настройка Предметов") + gui("sides.energy_config", "Настройка Энергии") gui("sides.top", "Верхняя сторона") gui("sides.bottom", "Нижняя сторона") diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt index 58f61a93d..20294fce6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.mc.otm.block.entity +import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableSet import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import it.unimi.dsi.fastutil.longs.Long2ObjectFunction @@ -52,7 +53,9 @@ import ru.dbotthepony.mc.otm.core.forValidRefs import ru.dbotthepony.mc.otm.core.get import ru.dbotthepony.mc.otm.core.getValue import ru.dbotthepony.mc.otm.core.ifPresentK +import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableMap +import ru.dbotthepony.mc.otm.core.immutableSet import ru.dbotthepony.mc.otm.core.math.BlockRotation import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.minus @@ -170,14 +173,16 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc } } - protected fun exposeEnergy(side: RelativeSide, value: IMatteryEnergyStorage) { - val thisSide = _sides[side]!! + protected fun exposeEnergy(side: RelativeSide, value: IMatteryEnergyStorage): ImmutableList> { + return immutableList { + val thisSide = _sides[side]!! - thisSide.Cap(ForgeCapabilities.ENERGY, value) - thisSide.Cap(MatteryCapability.ENERGY, value) + accept(thisSide.Cap(ForgeCapabilities.ENERGY, value)) + accept(thisSide.Cap(MatteryCapability.ENERGY, value)) - if (isMekanismLoaded) { - thisSide.Cap(MatteryCapability.MEKANISM_ENERGY, Mattery2MekanismEnergyWrapper(value)) + if (isMekanismLoaded) { + accept(thisSide.Cap(MatteryCapability.MEKANISM_ENERGY, Mattery2MekanismEnergyWrapper(value))) + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryDeviceBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryDeviceBlockEntity.kt index 595d313b2..2159a4ab0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryDeviceBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryDeviceBlockEntity.kt @@ -15,12 +15,16 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.items.IItemHandler import ru.dbotthepony.mc.otm.capability.CombinedItemHandler import ru.dbotthepony.mc.otm.capability.EmptyItemHandler +import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.UnmodifiableItemHandler +import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.moveBetweenSlots +import ru.dbotthepony.mc.otm.capability.moveEnergy import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.getValue import ru.dbotthepony.mc.otm.core.ifPresentK import ru.dbotthepony.mc.otm.core.immutableMap +import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.nbt.getJson import ru.dbotthepony.mc.otm.core.nbt.putJson @@ -68,6 +72,135 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo redstoneControl.deserializeNBT(nbt[REDSTONE_CONTROL_KEY] as? CompoundTag) } + inner class ConfigurableEnergy( + val capability: T, + + val possibleModes: FlowDirection = capability.energyFlow, + + val frontDefault: FlowDirection = possibleModes, + val backDefault: FlowDirection = possibleModes, + val leftDefault: FlowDirection = possibleModes, + val rightDefault: FlowDirection = possibleModes, + val topDefault: FlowDirection = possibleModes, + val bottomDefault: FlowDirection = possibleModes, + ) { + init { + exposeEnergySideless(capability) + } + + val front = Piece(RelativeSide.FRONT).also { it.energyFlow = frontDefault } + val back = Piece(RelativeSide.BACK).also { it.energyFlow = backDefault } + val left = Piece(RelativeSide.LEFT).also { it.energyFlow = leftDefault } + val right = Piece(RelativeSide.RIGHT).also { it.energyFlow = rightDefault } + val top = Piece(RelativeSide.TOP).also { it.energyFlow = topDefault } + val bottom = Piece(RelativeSide.BOTTOM).also { it.energyFlow = bottomDefault } + + val pieces = immutableMap { + put(RelativeSide.FRONT, front) + put(RelativeSide.BACK, back) + put(RelativeSide.LEFT, left) + put(RelativeSide.RIGHT, right) + put(RelativeSide.TOP, top) + put(RelativeSide.BOTTOM, bottom) + } + + val defaults = immutableMap { + put(RelativeSide.FRONT, frontDefault) + put(RelativeSide.BACK, backDefault) + put(RelativeSide.LEFT, leftDefault) + put(RelativeSide.RIGHT, rightDefault) + put(RelativeSide.TOP, topDefault) + put(RelativeSide.BOTTOM, bottomDefault) + } + + inner class Piece(val side: RelativeSide) : IMatteryEnergyStorage, ITickable { + private val capControllers = exposeEnergy(side, this@Piece) + private val neighbour by sides[side]!!.track(ForgeCapabilities.ENERGY) + + override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal { + return capability.extractEnergy(howMuch, simulate) + } + + override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal { + return capability.receiveEnergy(howMuch, simulate) + } + + override fun extractEnergyChecked(howMuch: Decimal, simulate: Boolean): Decimal { + if (energyFlow.output) + return capability.extractEnergyChecked(howMuch, simulate) + + return Decimal.ZERO + } + + override fun receiveEnergyChecked(howMuch: Decimal, simulate: Boolean): Decimal { + if (energyFlow.input) + return capability.receiveEnergyChecked(howMuch, simulate) + + return Decimal.ZERO + } + + override var batteryLevel: Decimal by capability::batteryLevel + override val maxBatteryLevel: Decimal by capability::maxBatteryLevel + override val missingPower: Decimal by capability::missingPower + + override val canSetBatteryLevel: Boolean by capability::canSetBatteryLevel + + override fun drainBattery(): Boolean { + return capability.drainBattery() + } + + override fun fillBattery(): Boolean { + return capability.fillBattery() + } + + override fun tick() { + if (energyFlow == FlowDirection.NONE || !automatePull && !automatePush) + return + + neighbour.ifPresentK { + if (energyFlow.input && automatePull) { + moveEnergy(source = it, destination = capability, simulate = false) + } + + if (energyFlow.output && automatePush) { + moveEnergy(source = capability, destination = it, simulate = false) + } + } + } + + init { + tickList.always(this) + } + + override var energyFlow by synchronizer.enum(possibleModes, setter = { value, access, setByRemote -> + require(possibleModes.isSupertype(value)) { "Energy mode $value is not allowed (allowed modes: ${possibleModes.family})" } + + if (access.read() != value) { + access.write(value) + + if (value == FlowDirection.NONE) { + for (controller in capControllers) + controller.close() + } else { + for (controller in capControllers) { + controller.close() + controller.expose() + } + } + } + }) + + var automatePull by synchronizer.bool() + var automatePush by synchronizer.bool() + + init { + savetables.enum(::energyFlow, "energy_${side}_flow", FlowDirection::valueOf) + savetables.bool(::automatePull, "energy_${side}_pull") + savetables.bool(::automatePush, "energy_${side}_push") + } + } + } + enum class ItemHandlerMode(val translationKey: String) { DISABLED("otm.gui.side_mode.disabled"), INPUT("otm.gui.side_mode.input"), @@ -107,7 +240,7 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo val top = Piece(RelativeSide.TOP, input, output, battery).also { it.mode = topDefault } val bottom = Piece(RelativeSide.BOTTOM, input, output, battery).also { it.mode = bottomDefault } - val sides = immutableMap { + val pieces = immutableMap { put(RelativeSide.FRONT, front) put(RelativeSide.BACK, back) put(RelativeSide.LEFT, left) @@ -126,14 +259,14 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo } inner class Piece( - side: RelativeSide, + val side: RelativeSide, val input: IItemHandler? = null, val output: IItemHandler? = null, val battery: IItemHandler? = null, ) : IItemHandler, ITickable { private var currentHandler: IItemHandler = EmptyItemHandler - val capController = this@MatteryDeviceBlockEntity.sides[side]!!.Cap(ForgeCapabilities.ITEM_HANDLER, this) - private val neighbour by this@MatteryDeviceBlockEntity.sides[side]!!.track(ForgeCapabilities.ITEM_HANDLER) + private val capController = sides[side]!!.Cap(ForgeCapabilities.ITEM_HANDLER, this) + private val neighbour by sides[side]!!.track(ForgeCapabilities.ITEM_HANDLER) val possibleViews: ImmutableSet val inputOutput: IItemHandler? @@ -184,29 +317,27 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo } }) - var automatePull = false - set(value) { - if (field != value) { - field = value + var automatePull by synchronizer.bool(setter = { value, access, _ -> + if (access.read() != value) { + access.write(value) - if (value) { - innerSlotPull = 0 - outerSlotPull = 0 - } + if (value) { + innerSlotPush = 0 + outerSlotPush = 0 } } + }) - var automatePush = false - set(value) { - if (field != value) { - field = value + var automatePush by synchronizer.bool(setter = { value, access, _ -> + if (access.read() != value) { + access.write(value) - if (value) { - innerSlotPush = 0 - outerSlotPush = 0 - } + if (value) { + innerSlotPush = 0 + outerSlotPush = 0 } } + }) init { savetables.bool(::automatePull, "itemhandler_${side}_automatePull") diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/ChemicalGeneratorBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/ChemicalGeneratorBlockEntity.kt index 9caad79cb..e1f8696a2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/ChemicalGeneratorBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/ChemicalGeneratorBlockEntity.kt @@ -47,20 +47,21 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe battery = batteryItemHandler, backDefault = ItemHandlerMode.BATTERY) + val energyConfig = ConfigurableEnergy(energy) + init { + for (piece in energyConfig.pieces.values) + piece.automatePush = true + savetable(::energy, ENERGY_KEY) savetable(::batteryContainer) savetable(::residueContainer) savetable(::fuelContainer) - exposeEnergyGlobally(energy) - savetables.int(::workTicks, WORK_TICKS_KEY) savetables.int(::workTicksTotal, WORK_TICKS_TOTAL_KEY) } - private val consumers = trackGlobally(ForgeCapabilities.ENERGY) - override fun setChangedLight() { super.setChangedLight() checkFuelSlot = true @@ -74,15 +75,6 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe private var checkFuelSlot = true - private fun workWithPower(it: IEnergyStorage) { - val extracted = energy.extractEnergy(THROUGHPUT, true) - val received = it.receiveEnergy(extracted, false) - - if (!received.isZero) { - energy.extractEnergy(received, false) - } - } - override fun tick() { super.tick() @@ -133,12 +125,9 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe val item = batteryContainer[0] if (!item.isEmpty) { - item.energy?.let(this::workWithPower) - if (energy.batteryLevel.isZero) return - } - - for (consumer in consumers) { - consumer.ifPresentK(::workWithPower) + item.energy?.also { + moveEnergy(energy, it, THROUGHPUT, simulate = false) + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/FlowDirection.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/FlowDirection.kt index d49194b2d..b26b06f06 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/FlowDirection.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/FlowDirection.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.mc.otm.capability +import com.google.common.collect.ImmutableSet import java.util.function.Predicate /** @@ -20,35 +21,84 @@ import java.util.function.Predicate * * `BI_DIRECTIONAL.test(OUTPUT)` = `false` * * `BI_DIRECTIONAL.test(INPUT)` = `false` */ -enum class FlowDirection(val input: Boolean, val output: Boolean) : Predicate { +enum class FlowDirection(val input: Boolean, val output: Boolean, val translationKey: String) : Predicate { /** * Can only be inputted (consumer) */ - INPUT(true, false), + INPUT(true, false, "otm.gui.side_mode.input"), /** * Can only be outputted/transmitted (producer) */ - OUTPUT(false, true), + OUTPUT(false, true, "otm.gui.side_mode.output"), /** * Can both consume and produce (capacitor) */ - BI_DIRECTIONAL(true, true), + BI_DIRECTIONAL(true, true, "otm.gui.side_mode.input_output"), /** * Why would you want to use this */ - NONE(false, false); + NONE(false, false, "otm.gui.side_mode.disabled"); + /** + * All values that pass [isSupertype] + */ + val family: ImmutableSet by lazy { + ImmutableSet.Builder().also { + when (this) { + INPUT -> it.add(INPUT) + OUTPUT -> it.add(OUTPUT) + BI_DIRECTIONAL -> { + it.add(INPUT) + it.add(OUTPUT) + it.add(BI_DIRECTIONAL) + } + else -> {} + } + + it.add(NONE) + }.build() + } + + /** + * Subtype test (returns true if we can assign [t] to this, e.g. we can assign [BI_DIRECTIONAL] to [INPUT]) + */ override fun test(t: FlowDirection): Boolean { return t === this || (!input || t.input) && (!output || t.output) } + /** + * Subtype test (returns true if we can assign [value] to this, e.g. we can assign [BI_DIRECTIONAL] to [INPUT]) + */ + fun isSubtype(value: FlowDirection) = test(value) + + /** + * Supertype test (e.g. [INPUT] is supertype to [BI_DIRECTIONAL], so if this is [BI_DIRECTIONAL] then calling [isSupertype] with [INPUT] will return `true`) + */ + fun isSupertype(value: FlowDirection): Boolean { + return value === this || value in family + } + fun intersect(other: FlowDirection): FlowDirection { return of(other.input && input, other.output && output) } + fun withInput(flag: Boolean): FlowDirection { + if (flag == input) + return this + + return of(flag, output) + } + + fun withOutput(flag: Boolean): FlowDirection { + if (flag == output) + return this + + return of(input, flag) + } + companion object { @JvmStatic fun of(input: Boolean, output: Boolean): FlowDirection { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Helpers.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Helpers.kt index cc8cb0e75..dafd77dfc 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Helpers.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Helpers.kt @@ -1,6 +1,8 @@ package ru.dbotthepony.mc.otm.capability +import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.items.IItemHandler +import ru.dbotthepony.mc.otm.core.math.Decimal /** * Attempts to safely exchange/move item between slots of two handlers @@ -41,3 +43,27 @@ fun moveBetweenSlots(source: IItemHandler, sourceSlot: Int, destination: IItemHa return sourceSlot to destinationSlot } + +@Suppress("name_shadowing") +fun moveEnergy(source: IEnergyStorage, destination: IEnergyStorage, amount: Decimal = Decimal.LONG_MAX_VALUE, simulate: Boolean): Decimal { + val extracted = source.extractEnergy(amount, true) + + if (extracted.isPositive) { + val received = destination.receiveEnergy(extracted, true) + + if (received.isPositive) { + val extracted = source.extractEnergy(received, true) + + if (extracted.isPositive) { + if (simulate) { + return extracted + } + + val received = destination.receiveEnergy(extracted, false) + return source.extractEnergy(received, false) + } + } + } + + return Decimal.ZERO +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt index 3bc229a6c..1b0a14618 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt @@ -68,4 +68,5 @@ object Widgets18 { val INPUT_OUTPUT = controlsGrid.next() val BATTERY_ONLY = controlsGrid.next() val ITEMS_CONFIGURATION = controlsGrid.next() + val ENERGY_CONFIGURATION = controlsGrid.next() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt index 80614aa6c..0902b3524 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/button/Buttons.kt @@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.client.screen.panels.button import com.mojang.blaze3d.platform.InputConstants 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.Widgets18 import ru.dbotthepony.mc.otm.client.screen.MatteryScreen @@ -10,6 +11,8 @@ import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.math.RelativeSide +import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback +import ru.dbotthepony.mc.otm.menu.input.EnergyPlayerInput import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.ItemHandlerPlayerInput import java.util.function.Predicate @@ -59,57 +62,33 @@ private fun > makeItemModeButton(screen: S, parent: FramePa return button } -private fun > makeItemHandlerControlPanel( - screen: S, - inputs: ItemHandlerPlayerInput -): FramePanel { - val frame = object : FramePanel(screen, 78f, 80f, TranslatableComponent("otm.gui.sides.item_config")) { - override fun tickInner() { - super.tickInner() +private fun > makeEnergyModeButton(screen: S, parent: FramePanel, input: EnergyPlayerInput.Piece): LargeEnumRectangleButtonPanel { + val button = LargeEnumRectangleButtonPanel(screen, parent, enum = FlowDirection::class.java, prop = input.input, defaultValue = input.default) - if (!isEverFocused()) { - remove() - } - } + val values = listOf( + FlowDirection.NONE to Widgets18.DISABLED, + FlowDirection.INPUT to Widgets18.INPUT_ONLY, + FlowDirection.OUTPUT to Widgets18.OUTPUT_ONLY, + FlowDirection.BI_DIRECTIONAL to Widgets18.INPUT_OUTPUT, + ) + + for ((k, v) in values) { + button.add(k, skinElement = v, tooltip = TranslatableComponent(k.translationKey)) } - val front = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.FRONT]!!) - val back = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.BACK]!!) - val left = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.LEFT]!!) - val right = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.RIGHT]!!) - val top = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.TOP]!!) - val bottom = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.BOTTOM]!!) + button.finish() - if (inputs.pull.test(minecraft.player)) { - val pull = LargeBooleanRectangleButtonPanel( - screen, - frame, - skinElementActive = Widgets18.PULL, - skinElementInactive = Widgets18.PULL_DISABLED, - prop = inputs.pull, - ) - - pull.tooltip = TranslatableComponent("otm.gui.side_mode.pull") - - pull.x = 30f - 20f - pull.y = 14f - } - - if (inputs.push.test(minecraft.player)) { - val push = LargeBooleanRectangleButtonPanel( - screen, - frame, - skinElementActive = Widgets18.PUSH, - skinElementInactive = Widgets18.PUSH_DISABLED, - prop = inputs.push, - ) - - push.tooltip = TranslatableComponent("otm.gui.side_mode.push") - - push.x = 30f + 20f - push.y = 14f - } + return button +} +private fun moveButtons( + front: EditablePanel<*>, + back: EditablePanel<*>, + left: EditablePanel<*>, + right: EditablePanel<*>, + top: EditablePanel<*>, + bottom: EditablePanel<*>, +) { top.tooltip = TranslatableComponent("otm.gui.sides.top") bottom.tooltip = TranslatableComponent("otm.gui.sides.bottom") back.tooltip = TranslatableComponent("otm.gui.sides.back") @@ -134,9 +113,94 @@ private fun > makeItemHandlerControlPanel( back.x = 30f - 20f back.y = 14f + 42f +} +@Suppress("name_shadowing") +private fun pullPush(frame: FramePanel<*>, pull: BooleanInputWithFeedback, push: BooleanInputWithFeedback) { + if (pull.test(minecraft.player)) { + val pull = LargeBooleanRectangleButtonPanel( + frame.screen, + frame, + skinElementActive = Widgets18.PULL, + skinElementInactive = Widgets18.PULL_DISABLED, + prop = pull, + ) + + pull.tooltip = TranslatableComponent("otm.gui.side_mode.pull") + + pull.x = 30f - 20f + pull.y = 14f + } + + if (push.test(minecraft.player)) { + val push = LargeBooleanRectangleButtonPanel( + frame.screen, + frame, + skinElementActive = Widgets18.PUSH, + skinElementInactive = Widgets18.PUSH_DISABLED, + prop = push, + ) + + push.tooltip = TranslatableComponent("otm.gui.side_mode.push") + + push.x = 30f + 20f + push.y = 14f + } +} + +private fun > makeItemHandlerControlPanel( + screen: S, + inputs: ItemHandlerPlayerInput +): FramePanel { + val frame = object : FramePanel(screen, 78f, 80f, TranslatableComponent("otm.gui.sides.item_config")) { + override fun tickInner() { + super.tickInner() + + if (!isEverFocused()) { + remove() + } + } + } + + val front = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.FRONT]!!) + val back = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.BACK]!!) + val left = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.LEFT]!!) + val right = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.RIGHT]!!) + val top = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.TOP]!!) + val bottom = makeItemModeButton(screen, frame, inputs.pieces[RelativeSide.BOTTOM]!!) + + pullPush(frame, inputs.pull, inputs.push) + moveButtons(front, back, left, right, top, bottom) screen.addPanel(frame) + frame.requestFocus() + return frame +} + +private fun > makeEnergyConfigPanel( + screen: S, + inputs: EnergyPlayerInput +): FramePanel { + val frame = object : FramePanel(screen, 78f, 80f, TranslatableComponent("otm.gui.sides.energy_config")) { + override fun tickInner() { + super.tickInner() + + if (!isEverFocused()) { + remove() + } + } + } + + val front = makeEnergyModeButton(screen, frame, inputs.pieces[RelativeSide.FRONT]!!).also { it.predicate = Predicate { inputs.possibleModes.isSupertype(it) } } + val back = makeEnergyModeButton(screen, frame, inputs.pieces[RelativeSide.BACK]!!).also { it.predicate = Predicate { inputs.possibleModes.isSupertype(it) } } + val left = makeEnergyModeButton(screen, frame, inputs.pieces[RelativeSide.LEFT]!!).also { it.predicate = Predicate { inputs.possibleModes.isSupertype(it) } } + val right = makeEnergyModeButton(screen, frame, inputs.pieces[RelativeSide.RIGHT]!!).also { it.predicate = Predicate { inputs.possibleModes.isSupertype(it) } } + val top = makeEnergyModeButton(screen, frame, inputs.pieces[RelativeSide.TOP]!!).also { it.predicate = Predicate { inputs.possibleModes.isSupertype(it) } } + val bottom = makeEnergyModeButton(screen, frame, inputs.pieces[RelativeSide.BOTTOM]!!).also { it.predicate = Predicate { inputs.possibleModes.isSupertype(it) } } + + pullPush(frame, inputs.pull, inputs.push) + moveButtons(front, back, left, right, top, bottom) + screen.addPanel(frame) frame.requestFocus() return frame @@ -146,7 +210,8 @@ fun > makeDeviceControls( screen: S, parent: FramePanel, redstone: IPlayerInputWithFeedback? = null, - itemConfig: ItemHandlerPlayerInput? = null + itemConfig: ItemHandlerPlayerInput? = null, + energyConfig: EnergyPlayerInput? = null, ): EditablePanel { val panel = object : EditablePanel(screen, parent, width = LargeEnumRectangleButtonPanel.SIZE, height = 0f, x = parent.width + 3f) { override fun tickInner() { @@ -179,6 +244,23 @@ fun > makeDeviceControls( }.height + 2f } + if (energyConfig != null) { + y += object : LargeRectangleButtonPanel(screen, panel, y = y, skinElement = Widgets18.ENERGY_CONFIGURATION) { + init { + tooltip = TranslatableComponent("otm.gui.sides.energy_config") + } + + override fun onClick(mouseButton: Int) { + if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) { + val frame = makeEnergyConfigPanel(screen, energyConfig) + + frame.x = absoluteX + width / 2f - frame.width / 2f + frame.y = absoluteY + height + 8f + } + } + }.height + 2f + } + panel.height = (y - 2f).coerceAtLeast(0f) return panel } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/ChemicalGeneratorScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/ChemicalGeneratorScreen.kt index 1d12426f1..64de1b85b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/ChemicalGeneratorScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/ChemicalGeneratorScreen.kt @@ -34,7 +34,7 @@ class ChemicalGeneratorScreen(menu: ChemicalGeneratorMenu, inventory: Inventory, SlotPanel(this, frame, menu.residueSlot, 56f, PROGRESS_SLOT_TOP) SlotPanel(this, frame, menu.fuelSlot, 104f, PROGRESS_SLOT_TOP) - makeDeviceControls(this, frame, redstone = menu.redstone, itemConfig = menu.itemConfig) + makeDeviceControls(this, frame, redstone = menu.redstone, itemConfig = menu.itemConfig, energyConfig = menu.energyConfig) return frame } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index fc6e51c1b..4ca99c91e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -5,6 +5,7 @@ package ru.dbotthepony.mc.otm.core import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableMap +import com.google.common.collect.ImmutableSet import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonPrimitive @@ -35,6 +36,7 @@ import java.math.BigInteger import java.util.Arrays import java.util.Spliterators import java.util.UUID +import java.util.function.Consumer import java.util.stream.Stream import java.util.stream.StreamSupport import kotlin.reflect.KProperty @@ -135,6 +137,18 @@ inline fun immutableMap(initializer: ImmutableMap.Builder immutableSet(initializer: Consumer.() -> Unit): ImmutableSet { + val builder = ImmutableSet.Builder() + initializer.invoke(builder::add) + return builder.build() +} + +inline fun immutableList(initializer: Consumer.() -> Unit): ImmutableList { + val builder = ImmutableList.Builder() + initializer.invoke(builder::add) + return builder.build() +} + fun IForgeRegistry.getID(value: T): Int { return (this as ForgeRegistry).getID(value) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/EnergyPlayerInput.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/EnergyPlayerInput.kt new file mode 100644 index 000000000..c74ebac05 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/EnergyPlayerInput.kt @@ -0,0 +1,62 @@ +package ru.dbotthepony.mc.otm.menu.input + +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity +import ru.dbotthepony.mc.otm.capability.FlowDirection +import ru.dbotthepony.mc.otm.core.immutableMap +import ru.dbotthepony.mc.otm.core.math.RelativeSide +import ru.dbotthepony.mc.otm.menu.MatteryMenu + +/** + * [allowPull] and [allowPush] controls whenever player is allowed to change these options + */ +class EnergyPlayerInput(val menu: MatteryMenu, val allowPull: Boolean = true, val allowPush: Boolean = true) { + var possibleModes by menu.mSynchronizer.enum(FlowDirection::class.java) + private set + + inner class Piece(val side: RelativeSide) { + val pull = BooleanInputWithFeedback(menu) + val push = BooleanInputWithFeedback(menu) + val input = EnumInputWithFeedback(menu) + + var default by menu.mSynchronizer.enum(FlowDirection.NONE) + + init { + pull.filter { allowPull } + push.filter { allowPush } + } + + fun configure(config: MatteryDeviceBlockEntity.ConfigurableEnergy<*>.Piece, parent: MatteryDeviceBlockEntity.ConfigurableEnergy<*>) { + pull.with(config::automatePull) + push.with(config::automatePush) + input.withSupplier { config.energyFlow }.withConsumer { if (parent.possibleModes.isSupertype(it)) config.energyFlow = it } + } + } + + val pieces = immutableMap { for (side in RelativeSide.values()) put(side, Piece(side)) } + + // TODO + val pull = BooleanInputWithFeedback(menu) + + // TODO + val push = BooleanInputWithFeedback(menu) + + init { + pull.filter { allowPull } + push.filter { allowPush } + } + + fun configure(config: MatteryDeviceBlockEntity.ConfigurableEnergy<*>) { + possibleModes = config.possibleModes + + for ((side, v) in config.pieces) { + pieces[side]!!.configure(v, config) + pieces[side]!!.default = config.defaults[side]!! + } + + pull.withSupplier { pieces.values.all { it.pull.value } } + push.withSupplier { pieces.values.all { it.push.value } } + + pull.withConsumer { v -> pieces.values.forEach { it.pull.input.invoke(v) } } + push.withConsumer { v -> pieces.values.forEach { it.push.input.invoke(v) } } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/ItemHandlerPlayerInput.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/ItemHandlerPlayerInput.kt index ea5b9eae9..2706e5798 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/ItemHandlerPlayerInput.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/ItemHandlerPlayerInput.kt @@ -50,7 +50,7 @@ class ItemHandlerPlayerInput(val menu: MatteryMenu, val allowPull: Boolean = tru } fun configure(config: MatteryDeviceBlockEntity.ConfigurableItemHandler) { - for ((side, v) in config.sides) { + for ((side, v) in config.pieces) { pieces[side]!!.configure(v) pieces[side]!!.default = config.defaults[side]!! } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/ChemicalGeneratorMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/ChemicalGeneratorMenu.kt index 18ea8fc55..71f02822a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/ChemicalGeneratorMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/ChemicalGeneratorMenu.kt @@ -10,6 +10,7 @@ import ru.dbotthepony.mc.otm.block.entity.tech.ChemicalGeneratorBlockEntity import ru.dbotthepony.mc.otm.core.ifPresentK import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatterySlot +import ru.dbotthepony.mc.otm.menu.input.EnergyPlayerInput import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.ItemHandlerPlayerInput import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget @@ -21,11 +22,13 @@ class ChemicalGeneratorMenu @JvmOverloads constructor(id: Int, inv: Inventory, t val redstone = EnumInputWithFeedback(this, RedstoneSetting::class.java) val itemConfig = ItemHandlerPlayerInput(this, allowPull = false, allowPush = true) + val energyConfig = EnergyPlayerInput(this, allowPull = false, allowPush = true) init { if (tile != null) { redstone.with(tile.redstoneControl::redstoneSetting) itemConfig.configure(tile.itemConfig) + energyConfig.configure(tile.energyConfig) } } diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/side_controls.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/side_controls.png index 64b4f0f00..fafdeacc0 100644 Binary files a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/side_controls.png and b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/side_controls.png differ diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/side_controls.xcf b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/side_controls.xcf index 4be0c0acb..395567a73 100644 Binary files a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/side_controls.xcf and b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/side_controls.xcf differ