Configurable energy handler working

This commit is contained in:
DBotThePony 2023-02-21 09:13:31 +07:00
parent 9959f72db3
commit 3b53d65b29
Signed by: DBot
GPG Key ID: DCC23B5715498507
16 changed files with 464 additions and 99 deletions

View File

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

View File

@ -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", "Нижняя сторона")

View File

@ -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<Side.Cap<*>> {
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)))
}
}
}

View File

@ -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<T : IMatteryEnergyStorage>(
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<ItemHandlerMode>
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")

View File

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

View File

@ -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<FlowDirection> {
enum class FlowDirection(val input: Boolean, val output: Boolean, val translationKey: String) : Predicate<FlowDirection> {
/**
* 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<FlowDirection> by lazy {
ImmutableSet.Builder<FlowDirection>().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 {

View File

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

View File

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

View File

@ -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 <S : MatteryScreen<*>> makeItemModeButton(screen: S, parent: FramePa
return button
}
private fun <S : MatteryScreen<*>> makeItemHandlerControlPanel(
screen: S,
inputs: ItemHandlerPlayerInput
): FramePanel<S> {
val frame = object : FramePanel<S>(screen, 78f, 80f, TranslatableComponent("otm.gui.sides.item_config")) {
override fun tickInner() {
super.tickInner()
private fun <S : MatteryScreen<*>> makeEnergyModeButton(screen: S, parent: FramePanel<S>, input: EnergyPlayerInput.Piece): LargeEnumRectangleButtonPanel<S, FlowDirection> {
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 <S : MatteryScreen<*>> 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 <S : MatteryScreen<*>> makeItemHandlerControlPanel(
screen: S,
inputs: ItemHandlerPlayerInput
): FramePanel<S> {
val frame = object : FramePanel<S>(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 <S : MatteryScreen<*>> makeEnergyConfigPanel(
screen: S,
inputs: EnergyPlayerInput
): FramePanel<S> {
val frame = object : FramePanel<S>(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 <S : MatteryScreen<*>> makeDeviceControls(
screen: S,
parent: FramePanel<S>,
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null,
itemConfig: ItemHandlerPlayerInput? = 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() {
@ -179,6 +244,23 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
}.height + 2f
}
if (energyConfig != null) {
y += object : LargeRectangleButtonPanel<S>(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
}

View File

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

View File

@ -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 <K : Any, V : Any> immutableMap(initializer: ImmutableMap.Builder<K,
return builder.build()
}
inline fun <V : Any> immutableSet(initializer: Consumer<V>.() -> Unit): ImmutableSet<V> {
val builder = ImmutableSet.Builder<V>()
initializer.invoke(builder::add)
return builder.build()
}
inline fun <V : Any> immutableList(initializer: Consumer<V>.() -> Unit): ImmutableList<V> {
val builder = ImmutableList.Builder<V>()
initializer.invoke(builder::add)
return builder.build()
}
fun <T> IForgeRegistry<T>.getID(value: T): Int {
return (this as ForgeRegistry<T>).getID(value)
}

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB