Profiled energy storage!

This commit is contained in:
DBotThePony 2023-04-14 23:58:38 +07:00
parent 8c32a2cd7a
commit 8afa21ae16
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 309 additions and 8 deletions

View File

@ -264,6 +264,7 @@ private fun misc(provider: MatteryLanguageProvider) {
gui("power.percentage_level", "Energy level: %s%%")
gui("level", "%s / %s")
gui("diff", "%s (%s / %s)")
gui("power.name", "MtJ")
gui("fluid.name", "B")
gui("fluid.level", "%s / %s of %s")

View File

@ -270,7 +270,6 @@ private fun misc(provider: MatteryLanguageProvider) {
misc("pill.message_finish", "§kОДИН ИЗ НАС ОДИН ИЗ НАС ОДИН ИЗ НАС ОДИН ИЗ НАС ОДИН ИЗ НАС")
gui("power.percentage_level", "Уровень энергии: %s%%")
gui("level", "%s / %s")
gui("power.name", "МтДж")
gui("fluid.name", "В")
gui("fluid.level", "%s / %s с %s")

View File

@ -15,6 +15,7 @@ import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.core.*
@ -39,7 +40,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
val residueItemHandler = residueContainer.handler(HandlerFilter.OnlyOut)
val fuelItemHandler = fuelContainer.handler(HandlerFilter.ChemicalFuel)
val energy = GeneratorEnergyStorage(::setChangedLight, CAPACITY, THROUGHPUT)
val energy = ProfiledEnergyStorage(GeneratorEnergyStorage(::setChangedLight, CAPACITY, THROUGHPUT))
val itemConfig = ConfigurableItemHandler(
input = fuelItemHandler,
@ -77,6 +78,7 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
override fun tick() {
super.tick()
energy.tick()
if (workTicks > 0) {
workTicks--

View File

@ -0,0 +1,169 @@
package ru.dbotthepony.mc.otm.capability.energy
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NumericTag
import net.minecraft.nbt.Tag
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.set
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.ITickable
import java.util.*
import kotlin.collections.ArrayList
class ProfiledEnergyStorage<E : IMatteryEnergyStorage>(val parent: E) : IMatteryEnergyStorage, ITickable, INBTSerializable<CompoundTag?> {
override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
return recordTransfer(parent.extractEnergy(howMuch, simulate), simulate)
}
override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
return recordReceive(parent.receiveEnergy(howMuch, simulate), simulate)
}
override fun extractEnergyChecked(howMuch: Decimal, simulate: Boolean): Decimal {
return recordTransfer(parent.extractEnergyChecked(howMuch, simulate), simulate)
}
override fun receiveEnergyChecked(howMuch: Decimal, simulate: Boolean): Decimal {
return recordReceive(parent.receiveEnergyChecked(howMuch, simulate), simulate)
}
override val canSetBatteryLevel: Boolean get() = parent.canSetBatteryLevel
override val missingPower: Decimal get() = parent.missingPower
override fun drainBattery(): Boolean {
return parent.drainBattery()
}
override fun fillBattery(): Boolean {
return parent.fillBattery()
}
override var batteryLevel: Decimal
get() = parent.batteryLevel
set(value) { parent.batteryLevel = value }
override val maxBatteryLevel: Decimal get() = parent.maxBatteryLevel
override val energyFlow: FlowDirection get() = parent.energyFlow
var lastTickReceive = Decimal.ZERO
private set
var lastTickTransfer = Decimal.ZERO
private set
var tick = 0
private set
private val historyReceiveInternal = ArrayList<Decimal>()
private val historyTransferInternal = ArrayList<Decimal>()
val historyReceive: List<Decimal> = Collections.unmodifiableList(historyReceiveInternal)
val historyTransfer: List<Decimal> = Collections.unmodifiableList(historyTransferInternal)
init {
for (i in 0 until HISTORY_SIZE) {
historyReceiveInternal.add(Decimal.ZERO)
historyTransferInternal.add(Decimal.ZERO)
}
}
private fun recordTransfer(value: Decimal, simulate: Boolean): Decimal {
if (!simulate) {
lastTickTransfer += value
}
return value
}
private fun recordReceive(value: Decimal, simulate: Boolean): Decimal {
if (!simulate) {
lastTickReceive += value
}
return value
}
override fun tick() {
tick = (tick + 1) % HISTORY_SIZE
historyReceiveInternal[tick] = lastTickReceive
historyTransferInternal[tick] = lastTickTransfer
lastTickReceive = Decimal.ZERO
lastTickTransfer = Decimal.ZERO
}
override fun serializeNBT(): CompoundTag {
val tag: CompoundTag
if (parent is INBTSerializable<*>) {
tag = (parent as INBTSerializable<CompoundTag?>).serializeNBT() ?: CompoundTag()
} else {
tag = CompoundTag()
}
tag["historyReceive"] = ListTag().also {
for (value in historyReceiveInternal) {
it.add(value.serializeNBT())
}
}
tag["historyTransfer"] = ListTag().also {
for (value in historyTransferInternal) {
it.add(value.serializeNBT())
}
}
tag["historyTick"] = tick
tag["lastTickReceive"] = lastTickReceive
tag["lastTickTransfer"] = lastTickTransfer
return tag
}
override fun deserializeNBT(nbt: CompoundTag?) {
if (parent is INBTSerializable<*>) {
(parent as INBTSerializable<CompoundTag?>).deserializeNBT(nbt)
}
if (nbt != null) {
historyReceiveInternal.clear()
historyTransferInternal.clear()
for (i in 0 until HISTORY_SIZE) {
historyReceiveInternal.add(Decimal.ZERO)
historyTransferInternal.add(Decimal.ZERO)
}
nbt.map("historyTick") { it: NumericTag ->
tick = it.asInt
}
nbt.map("lastTickReceive") { it: Tag ->
lastTickReceive = Decimal.deserializeNBT(it)
}
nbt.map("lastTickTransfer") { it: Tag ->
lastTickReceive = Decimal.deserializeNBT(it)
}
nbt.map("historyReceive") { it: ListTag ->
for (i in 0 until HISTORY_SIZE.coerceAtMost(it.size)) {
historyReceiveInternal[i] = Decimal.deserializeNBT(it[i])
}
}
nbt.map("historyTransfer") { it: ListTag ->
for (i in 0 until HISTORY_SIZE.coerceAtMost(it.size)) {
historyTransferInternal[i] = Decimal.deserializeNBT(it[i])
}
}
}
}
companion object {
const val HISTORY_SIZE = 20
}
}

View File

@ -9,18 +9,17 @@ import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WidePowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu
class ChemicalGeneratorScreen(menu: ChemicalGeneratorMenu, inventory: Inventory, title: Component) : MatteryScreen<ChemicalGeneratorMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = super.makeMainFrame()!!
WidePowerGaugePanel(this, frame, menu.energy, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
WideProfiledPowerGaugePanel(this, frame, menu.energy, LEFT_MARGIN, GAUGE_TOP_WITH_SLOT)
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)
val self = this
val progress = object : ProgressGaugePanel<ChemicalGeneratorScreen>(self, frame, menu.progress, 78f, PROGRESS_ARROW_TOP) {
val progress = object : ProgressGaugePanel<ChemicalGeneratorScreen>(this@ChemicalGeneratorScreen, frame, menu.progress, 78f, PROGRESS_ARROW_TOP) {
override fun makeTooltip(): MutableList<Component> {
val list = super.makeTooltip()

View File

@ -1,16 +1,26 @@
package ru.dbotthepony.mc.otm.client.screen.widget
import com.mojang.blaze3d.vertex.PoseStack
import it.unimi.dsi.fastutil.ints.IntArrayList
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.client.render.*
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.stream
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.core.util.formatPowerLevel
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
open class PowerGaugePanel<out S : Screen> @JvmOverloads constructor(
open class PowerGaugePanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>? = null,
val widget: LevelGaugeWidget,
@ -73,6 +83,7 @@ open class PowerGaugePanel<out S : Screen> @JvmOverloads constructor(
/**
* Shortcut to [PowerGaugePanel] with doubled width
*/
@Suppress("FunctionName")
fun <S : Screen> WidePowerGaugePanel(
screen: S,
parent: EditablePanel<*>? = null,
@ -82,3 +93,78 @@ fun <S : Screen> WidePowerGaugePanel(
width: Float = 18f,
height: Float = 48f
) = PowerGaugePanel(screen, parent, widget, x, y, width, height)
private fun formatLevel(a: Decimal, b: Decimal): Component {
val diff = a - b
val fa = a.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN)
val fb = b.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_RED)
if (diff.isZero) {
return TranslatableComponent("otm.gui.diff", diff.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.GRAY), fa, fb)
} else if (diff.isPositive) {
return TranslatableComponent("otm.gui.diff", diff.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN), fa, fb)
} else {
return TranslatableComponent("otm.gui.diff", (-diff).formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_RED), fa, fb)
}
}
open class ProfiledPowerGaugePanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>? = null,
val profiledWidget: ProfiledEnergyGaugeWidget,
x: Float = 0f,
y: Float = 0f,
width: Float = GAUGE_BACKGROUND.width,
height: Float = GAUGE_BACKGROUND.height
) : PowerGaugePanel<S>(screen, parent, profiledWidget.gauge, x, y, width, height) {
override fun makeTooltip(): MutableList<Component> {
return super.makeTooltip().also {
it.add(TextComponent(""))
if (minecraft.window.isShiftDown) {
it.add(formatLevel(profiledWidget.lastTickReceive, profiledWidget.lastTickTransfer))
it.add(TextComponent("---"))
}
it.add(formatLevel(
profiledWidget.historyReceive
.stream()
.map { it.value }
.reduce(Decimal.ZERO) { a, b -> a + b }
.div(profiledWidget.historyReceive.size),
profiledWidget.historyTransfer.stream()
.map { it.value }
.reduce(Decimal.ZERO) { a, b -> a + b }
.div(profiledWidget.historyReceive.size),
))
if (minecraft.window.isShiftDown && minecraft.options.advancedItemTooltips) {
it.add(TextComponent("---"))
val values = IntArrayList()
values.addAll(profiledWidget.tick downTo 0)
values.addAll(ProfiledEnergyStorage.HISTORY_SIZE - 1 downTo profiledWidget.tick + 1)
for (i in values.intIterator()) {
it.add(formatLevel(profiledWidget.historyReceive[i].value, profiledWidget.historyTransfer[i].value))
}
}
}
}
}
/**
* Shortcut to [ProfiledPowerGaugePanel] with doubled width
*/
@Suppress("FunctionName")
fun <S : Screen> WideProfiledPowerGaugePanel(
screen: S,
parent: EditablePanel<*>? = null,
widget: ProfiledEnergyGaugeWidget,
x: Float = 0f,
y: Float = 0f,
width: Float = 18f,
height: Float = 48f
) = ProfiledPowerGaugePanel(screen, parent, widget, x, y, width, height)

View File

@ -14,6 +14,7 @@ import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledEnergyGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProgressGaugeWidget
import ru.dbotthepony.mc.otm.registry.MMenus
@ -53,7 +54,7 @@ class ChemicalGeneratorMenu @JvmOverloads constructor(id: Int, inv: Inventory, t
}
val progress = ProgressGaugeWidget(this)
val energy = LevelGaugeWidget(this, tile?.energy)
val energy = ProfiledEnergyGaugeWidget(this, tile?.energy)
var burnTime by mSynchronizer.int().property
init {

View File

@ -0,0 +1,44 @@
package ru.dbotthepony.mc.otm.menu.widget
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.DecimalValueCodec
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.network.synchronizer.FieldSynchronizer
class ProfiledEnergyGaugeWidget(synchronizer: FieldSynchronizer, val gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)) {
var parent: ProfiledEnergyStorage<*>? = null
private set
val historyReceive = immutableList(ProfiledEnergyStorage.HISTORY_SIZE) {
synchronizer.ComputedField({ parent?.historyReceive?.get(it) ?: Decimal.ZERO }, DecimalValueCodec)
}
val historyTransfer = immutableList(ProfiledEnergyStorage.HISTORY_SIZE) {
synchronizer.ComputedField({ parent?.historyTransfer?.get(it) ?: Decimal.ZERO }, DecimalValueCodec)
}
val tick by synchronizer.ComputedIntField({ parent?.tick ?: 0 }).property
val lastTickReceive by synchronizer.ComputedField({ parent?.lastTickReceive ?: Decimal.ZERO }, DecimalValueCodec)
val lastTickTransfer by synchronizer.ComputedField({ parent?.lastTickTransfer ?: Decimal.ZERO }, DecimalValueCodec)
constructor(synchronizer: FieldSynchronizer, storage: ProfiledEnergyStorage<*>?, gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)) : this(synchronizer, gauge = gauge) {
if (storage != null) {
with(storage)
}
}
constructor(menu: MatteryMenu, storage: ProfiledEnergyStorage<*>?, gauge: LevelGaugeWidget = LevelGaugeWidget(menu)) : this(menu.mSynchronizer, storage, gauge = gauge)
constructor(menu: MatteryMenu, gauge: LevelGaugeWidget = LevelGaugeWidget(menu)) : this(menu.mSynchronizer, gauge = gauge)
constructor(menu: MatteryPoweredMenu, storage: ProfiledEnergyStorage<*>?) : this(menu.mSynchronizer, storage, menu.powerWidget)
constructor(menu: MatteryPoweredMenu) : this(menu.mSynchronizer, menu.powerWidget)
fun with(storage: ProfiledEnergyStorage<*>): ProfiledEnergyGaugeWidget {
gauge.with(storage)
parent = storage
return this
}
}