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("stored_amount", "Exact amount stored: %s")
gui("sides.item_config", "Item Configuration") gui("sides.item_config", "Item Configuration")
gui("sides.energy_config", "Energy Configuration")
gui("sides.top", "Top") gui("sides.top", "Top")
gui("sides.bottom", "Bottom") gui("sides.bottom", "Bottom")

View File

@ -618,6 +618,7 @@ private fun gui(provider: MatteryLanguageProvider) {
gui("stored_amount", "Точное количество в хранилище: %s шт.") gui("stored_amount", "Точное количество в хранилище: %s шт.")
gui("sides.item_config", "Настройка Предметов") gui("sides.item_config", "Настройка Предметов")
gui("sides.energy_config", "Настройка Энергии")
gui("sides.top", "Верхняя сторона") gui("sides.top", "Верхняя сторона")
gui("sides.bottom", "Нижняя сторона") gui("sides.bottom", "Нижняя сторона")

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.block.entity package ru.dbotthepony.mc.otm.block.entity
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet import com.google.common.collect.ImmutableSet
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction 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.get
import ru.dbotthepony.mc.otm.core.getValue import ru.dbotthepony.mc.otm.core.getValue
import ru.dbotthepony.mc.otm.core.ifPresentK 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.immutableMap
import ru.dbotthepony.mc.otm.core.immutableSet
import ru.dbotthepony.mc.otm.core.math.BlockRotation import ru.dbotthepony.mc.otm.core.math.BlockRotation
import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.minus 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) { protected fun exposeEnergy(side: RelativeSide, value: IMatteryEnergyStorage): ImmutableList<Side.Cap<*>> {
return immutableList {
val thisSide = _sides[side]!! val thisSide = _sides[side]!!
thisSide.Cap(ForgeCapabilities.ENERGY, value) accept(thisSide.Cap(ForgeCapabilities.ENERGY, value))
thisSide.Cap(MatteryCapability.ENERGY, value) accept(thisSide.Cap(MatteryCapability.ENERGY, value))
if (isMekanismLoaded) { if (isMekanismLoaded) {
thisSide.Cap(MatteryCapability.MEKANISM_ENERGY, Mattery2MekanismEnergyWrapper(value)) 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 net.minecraftforge.items.IItemHandler
import ru.dbotthepony.mc.otm.capability.CombinedItemHandler import ru.dbotthepony.mc.otm.capability.CombinedItemHandler
import ru.dbotthepony.mc.otm.capability.EmptyItemHandler 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.UnmodifiableItemHandler
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.moveBetweenSlots 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.TextComponent
import ru.dbotthepony.mc.otm.core.getValue import ru.dbotthepony.mc.otm.core.getValue
import ru.dbotthepony.mc.otm.core.ifPresentK import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.immutableMap 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.math.RelativeSide
import ru.dbotthepony.mc.otm.core.nbt.getJson import ru.dbotthepony.mc.otm.core.nbt.getJson
import ru.dbotthepony.mc.otm.core.nbt.putJson 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) 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) { enum class ItemHandlerMode(val translationKey: String) {
DISABLED("otm.gui.side_mode.disabled"), DISABLED("otm.gui.side_mode.disabled"),
INPUT("otm.gui.side_mode.input"), 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 top = Piece(RelativeSide.TOP, input, output, battery).also { it.mode = topDefault }
val bottom = Piece(RelativeSide.BOTTOM, input, output, battery).also { it.mode = bottomDefault } val bottom = Piece(RelativeSide.BOTTOM, input, output, battery).also { it.mode = bottomDefault }
val sides = immutableMap { val pieces = immutableMap {
put(RelativeSide.FRONT, front) put(RelativeSide.FRONT, front)
put(RelativeSide.BACK, back) put(RelativeSide.BACK, back)
put(RelativeSide.LEFT, left) put(RelativeSide.LEFT, left)
@ -126,14 +259,14 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
} }
inner class Piece( inner class Piece(
side: RelativeSide, val side: RelativeSide,
val input: IItemHandler? = null, val input: IItemHandler? = null,
val output: IItemHandler? = null, val output: IItemHandler? = null,
val battery: IItemHandler? = null, val battery: IItemHandler? = null,
) : IItemHandler, ITickable { ) : IItemHandler, ITickable {
private var currentHandler: IItemHandler = EmptyItemHandler private var currentHandler: IItemHandler = EmptyItemHandler
val capController = this@MatteryDeviceBlockEntity.sides[side]!!.Cap(ForgeCapabilities.ITEM_HANDLER, this) private val capController = sides[side]!!.Cap(ForgeCapabilities.ITEM_HANDLER, this)
private val neighbour by this@MatteryDeviceBlockEntity.sides[side]!!.track(ForgeCapabilities.ITEM_HANDLER) private val neighbour by sides[side]!!.track(ForgeCapabilities.ITEM_HANDLER)
val possibleViews: ImmutableSet<ItemHandlerMode> val possibleViews: ImmutableSet<ItemHandlerMode>
val inputOutput: IItemHandler? val inputOutput: IItemHandler?
@ -184,29 +317,27 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
} }
}) })
var automatePull = false var automatePull by synchronizer.bool(setter = { value, access, _ ->
set(value) { if (access.read() != value) {
if (field != value) { access.write(value)
field = value
if (value) {
innerSlotPull = 0
outerSlotPull = 0
}
}
}
var automatePush = false
set(value) {
if (field != value) {
field = value
if (value) { if (value) {
innerSlotPush = 0 innerSlotPush = 0
outerSlotPush = 0 outerSlotPush = 0
} }
} }
})
var automatePush by synchronizer.bool(setter = { value, access, _ ->
if (access.read() != value) {
access.write(value)
if (value) {
innerSlotPush = 0
outerSlotPush = 0
} }
}
})
init { init {
savetables.bool(::automatePull, "itemhandler_${side}_automatePull") savetables.bool(::automatePull, "itemhandler_${side}_automatePull")

View File

@ -47,20 +47,21 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
battery = batteryItemHandler, battery = batteryItemHandler,
backDefault = ItemHandlerMode.BATTERY) backDefault = ItemHandlerMode.BATTERY)
val energyConfig = ConfigurableEnergy(energy)
init { init {
for (piece in energyConfig.pieces.values)
piece.automatePush = true
savetable(::energy, ENERGY_KEY) savetable(::energy, ENERGY_KEY)
savetable(::batteryContainer) savetable(::batteryContainer)
savetable(::residueContainer) savetable(::residueContainer)
savetable(::fuelContainer) savetable(::fuelContainer)
exposeEnergyGlobally(energy)
savetables.int(::workTicks, WORK_TICKS_KEY) savetables.int(::workTicks, WORK_TICKS_KEY)
savetables.int(::workTicksTotal, WORK_TICKS_TOTAL_KEY) savetables.int(::workTicksTotal, WORK_TICKS_TOTAL_KEY)
} }
private val consumers = trackGlobally(ForgeCapabilities.ENERGY)
override fun setChangedLight() { override fun setChangedLight() {
super.setChangedLight() super.setChangedLight()
checkFuelSlot = true checkFuelSlot = true
@ -74,15 +75,6 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
private var checkFuelSlot = true 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() { override fun tick() {
super.tick() super.tick()
@ -133,12 +125,9 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
val item = batteryContainer[0] val item = batteryContainer[0]
if (!item.isEmpty) { if (!item.isEmpty) {
item.energy?.let(this::workWithPower) item.energy?.also {
if (energy.batteryLevel.isZero) return moveEnergy(energy, it, THROUGHPUT, simulate = false)
} }
for (consumer in consumers) {
consumer.ifPresentK(::workWithPower)
} }
} }

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.capability package ru.dbotthepony.mc.otm.capability
import com.google.common.collect.ImmutableSet
import java.util.function.Predicate import java.util.function.Predicate
/** /**
@ -20,35 +21,84 @@ import java.util.function.Predicate
* * `BI_DIRECTIONAL.test(OUTPUT)` = `false` * * `BI_DIRECTIONAL.test(OUTPUT)` = `false`
* * `BI_DIRECTIONAL.test(INPUT)` = `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) * Can only be inputted (consumer)
*/ */
INPUT(true, false), INPUT(true, false, "otm.gui.side_mode.input"),
/** /**
* Can only be outputted/transmitted (producer) * Can only be outputted/transmitted (producer)
*/ */
OUTPUT(false, true), OUTPUT(false, true, "otm.gui.side_mode.output"),
/** /**
* Can both consume and produce (capacitor) * 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 * 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 { override fun test(t: FlowDirection): Boolean {
return t === this || (!input || t.input) && (!output || t.output) 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 { fun intersect(other: FlowDirection): FlowDirection {
return of(other.input && input, other.output && output) 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 { companion object {
@JvmStatic @JvmStatic
fun of(input: Boolean, output: Boolean): FlowDirection { fun of(input: Boolean, output: Boolean): FlowDirection {

View File

@ -1,6 +1,8 @@
package ru.dbotthepony.mc.otm.capability package ru.dbotthepony.mc.otm.capability
import net.minecraftforge.energy.IEnergyStorage
import net.minecraftforge.items.IItemHandler import net.minecraftforge.items.IItemHandler
import ru.dbotthepony.mc.otm.core.math.Decimal
/** /**
* Attempts to safely exchange/move item between slots of two handlers * 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 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 INPUT_OUTPUT = controlsGrid.next()
val BATTERY_ONLY = controlsGrid.next() val BATTERY_ONLY = controlsGrid.next()
val ITEMS_CONFIGURATION = 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 com.mojang.blaze3d.platform.InputConstants
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
@ -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.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.RelativeSide 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.IPlayerInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemHandlerPlayerInput import ru.dbotthepony.mc.otm.menu.input.ItemHandlerPlayerInput
import java.util.function.Predicate import java.util.function.Predicate
@ -59,57 +62,33 @@ private fun <S : MatteryScreen<*>> makeItemModeButton(screen: S, parent: FramePa
return button return button
} }
private fun <S : MatteryScreen<*>> makeItemHandlerControlPanel( private fun <S : MatteryScreen<*>> makeEnergyModeButton(screen: S, parent: FramePanel<S>, input: EnergyPlayerInput.Piece): LargeEnumRectangleButtonPanel<S, FlowDirection> {
screen: S, val button = LargeEnumRectangleButtonPanel(screen, parent, enum = FlowDirection::class.java, prop = input.input, defaultValue = input.default)
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()) { val values = listOf(
remove() FlowDirection.NONE to Widgets18.DISABLED,
} FlowDirection.INPUT to Widgets18.INPUT_ONLY,
} FlowDirection.OUTPUT to Widgets18.OUTPUT_ONLY,
} FlowDirection.BI_DIRECTIONAL to Widgets18.INPUT_OUTPUT,
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]!!)
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") for ((k, v) in values) {
button.add(k, skinElement = v, tooltip = TranslatableComponent(k.translationKey))
pull.x = 30f - 20f
pull.y = 14f
} }
if (inputs.push.test(minecraft.player)) { button.finish()
val push = LargeBooleanRectangleButtonPanel(
screen,
frame,
skinElementActive = Widgets18.PUSH,
skinElementInactive = Widgets18.PUSH_DISABLED,
prop = inputs.push,
)
push.tooltip = TranslatableComponent("otm.gui.side_mode.push") return button
push.x = 30f + 20f
push.y = 14f
} }
private fun moveButtons(
front: EditablePanel<*>,
back: EditablePanel<*>,
left: EditablePanel<*>,
right: EditablePanel<*>,
top: EditablePanel<*>,
bottom: EditablePanel<*>,
) {
top.tooltip = TranslatableComponent("otm.gui.sides.top") top.tooltip = TranslatableComponent("otm.gui.sides.top")
bottom.tooltip = TranslatableComponent("otm.gui.sides.bottom") bottom.tooltip = TranslatableComponent("otm.gui.sides.bottom")
back.tooltip = TranslatableComponent("otm.gui.sides.back") back.tooltip = TranslatableComponent("otm.gui.sides.back")
@ -134,9 +113,94 @@ private fun <S : MatteryScreen<*>> makeItemHandlerControlPanel(
back.x = 30f - 20f back.x = 30f - 20f
back.y = 14f + 42f 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) 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() frame.requestFocus()
return frame return frame
@ -146,7 +210,8 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
screen: S, screen: S,
parent: FramePanel<S>, parent: FramePanel<S>,
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null, redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null,
itemConfig: ItemHandlerPlayerInput? = null itemConfig: ItemHandlerPlayerInput? = null,
energyConfig: EnergyPlayerInput? = null,
): EditablePanel<S> { ): EditablePanel<S> {
val panel = object : EditablePanel<S>(screen, parent, width = LargeEnumRectangleButtonPanel.SIZE, height = 0f, x = parent.width + 3f) { val panel = object : EditablePanel<S>(screen, parent, width = LargeEnumRectangleButtonPanel.SIZE, height = 0f, x = parent.width + 3f) {
override fun tickInner() { override fun tickInner() {
@ -179,6 +244,23 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
}.height + 2f }.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) panel.height = (y - 2f).coerceAtLeast(0f)
return panel 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.residueSlot, 56f, PROGRESS_SLOT_TOP)
SlotPanel(this, frame, menu.fuelSlot, 104f, 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 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.ImmutableList
import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive import com.google.gson.JsonPrimitive
@ -35,6 +36,7 @@ import java.math.BigInteger
import java.util.Arrays import java.util.Arrays
import java.util.Spliterators import java.util.Spliterators
import java.util.UUID import java.util.UUID
import java.util.function.Consumer
import java.util.stream.Stream import java.util.stream.Stream
import java.util.stream.StreamSupport import java.util.stream.StreamSupport
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@ -135,6 +137,18 @@ inline fun <K : Any, V : Any> immutableMap(initializer: ImmutableMap.Builder<K,
return builder.build() 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 { fun <T> IForgeRegistry<T>.getID(value: T): Int {
return (this as ForgeRegistry<T>).getID(value) 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) { fun configure(config: MatteryDeviceBlockEntity.ConfigurableItemHandler) {
for ((side, v) in config.sides) { for ((side, v) in config.pieces) {
pieces[side]!!.configure(v) pieces[side]!!.configure(v)
pieces[side]!!.default = config.defaults[side]!! 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.core.ifPresentK
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.input.EnergyPlayerInput
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemHandlerPlayerInput import ru.dbotthepony.mc.otm.menu.input.ItemHandlerPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget 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 redstone = EnumInputWithFeedback(this, RedstoneSetting::class.java)
val itemConfig = ItemHandlerPlayerInput(this, allowPull = false, allowPush = true) val itemConfig = ItemHandlerPlayerInput(this, allowPull = false, allowPush = true)
val energyConfig = EnergyPlayerInput(this, allowPull = false, allowPush = true)
init { init {
if (tile != null) { if (tile != null) {
redstone.with(tile.redstoneControl::redstoneSetting) redstone.with(tile.redstoneControl::redstoneSetting)
itemConfig.configure(tile.itemConfig) 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