From a6ff93d74c7e7683255312e0e8a594bad05903b4 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 22 Sep 2024 17:51:15 +0700 Subject: [PATCH] "Diagram" basic rendering code, added gui icons for energy counter --- .../entity/tech/EnergyCounterBlockEntity.kt | 101 ++++++-------- .../otm/client/render/DynamicBufferSource.kt | 3 - .../{FontRenderer.kt => FontRendering.kt} | 54 +++++++- .../mc/otm/client/render/GraphRendering.kt | 107 +++++++++++++++ .../mc/otm/client/render/IGUIRenderable.kt | 2 +- .../mc/otm/client/render/Widgets18.kt | 2 + .../blockentity/EnergyCounterRenderer.kt | 2 +- .../client/screen/ExopackInventoryScreen.kt | 8 +- .../client/screen/matter/MatterPanelScreen.kt | 4 +- .../panels/AbstractHistoryGraphPanel.kt | 19 +++ .../screen/panels/DecimalHistoryGraphPanel.kt | 54 ++++++++ .../mc/otm/client/screen/panels/FramePanel.kt | 128 +++++++++++------- .../screen/storage/DriveViewerScreen.kt | 4 +- .../client/screen/tech/EnergyCounterScreen.kt | 61 +++++---- .../mc/otm/core/AbstractHistoryGraph.kt | 4 - .../mc/otm/core/DecimalHistoryGraph.kt | 5 +- .../mc/otm/menu/tech/EnergyCounterMenu.kt | 31 +++-- .../textures/gui/widgets/misc18.png | Bin 826 -> 951 bytes .../textures/gui/widgets/misc18.xcf | Bin 3924 -> 5499 bytes 19 files changed, 423 insertions(+), 166 deletions(-) rename src/main/kotlin/ru/dbotthepony/mc/otm/client/render/{FontRenderer.kt => FontRendering.kt} (85%) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/render/GraphRendering.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/AbstractHistoryGraphPanel.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryGraphPanel.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyCounterBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyCounterBlockEntity.kt index 3db0703ee..628e5c377 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyCounterBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyCounterBlockEntity.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.mc.otm.block.entity.tech +import com.google.common.collect.ImmutableList import net.minecraft.core.BlockPos import net.minecraft.core.HolderLookup import net.minecraft.nbt.CompoundTag @@ -38,51 +39,32 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat BlockRotation.of(blockState[EnergyCounterBlock.INPUT_DIRECTION]) } - private val history = Array(10 * 20) { Decimal.ZERO } - internal var historyTick = 0 + val history5s = DecimalHistoryGraph(1, 100) + val history15s = DecimalHistoryGraph(3, 100) + val history1m = DecimalHistoryGraph(12, 100) + val history10m = DecimalHistoryGraph(120, 100) + val history1h = DecimalHistoryGraph(720, 100) + val history6h = DecimalHistoryGraph(720 * 6, 100) + val history24h = DecimalHistoryGraph(720 * 24, 100) + private val graphs = ImmutableList.of( + history5s, history15s, history1m, history10m, history1h, history6h, history24h, + ) + + init { + syncher.add(history5s) + } + + private var thisTick = Decimal.ZERO var lastTick by syncher.decimal() internal set var ioLimit: Decimal? = null fun resetStats() { - historyTick = 0 + graphs.forEach { it.clear() } lastTick = Decimal.ZERO passed = Decimal.ZERO - Arrays.fill(history, Decimal.ZERO) - } - - fun getHistory(ticks: Int): Array { - require(!(ticks < 1 || ticks >= history.size)) { "Invalid history length provided" } - - val history = Array(ticks) { Decimal.ZERO } - - for (i in 0 until ticks) { - var index = (historyTick - i) % this.history.size - if (index < 0) index = this.history.size - 1 - history[i] = this.history[index] - } - - return history - } - - fun calcAverage(ticks: Int): Decimal { - return sumHistory(ticks) / ticks - } - - fun sumHistory(ticks: Int): Decimal { - require(!(ticks < 1 || ticks >= history.size)) { "Invalid history length provided" } - - var value = Decimal.ZERO - - for (i in 0 until ticks) { - var index = (historyTick - i) % history.size - if (index < 0) index = history.size - 1 - value += history[index] - } - - return value } override fun saveShared(nbt: CompoundTag, registry: HolderLookup.Provider) { @@ -93,12 +75,14 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat override fun saveLevel(nbt: CompoundTag, registry: HolderLookup.Provider) { super.saveLevel(nbt, registry) - val list = ListTag() - nbt[POWER_HISTORY_KEY] = list - nbt[POWER_HISTORY_POINTER_KEY] = historyTick - for (num in history) - list.add(num.serializeNBT()) + nbt["history5s"] = history5s.serializeNBT(registry) + nbt["history15s"] = history15s.serializeNBT(registry) + nbt["history1m"] = history1m.serializeNBT(registry) + nbt["history10m"] = history10m.serializeNBT(registry) + nbt["history1h"] = history1h.serializeNBT(registry) + nbt["history6h"] = history6h.serializeNBT(registry) + nbt["history24h"] = history24h.serializeNBT(registry) } override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) { @@ -107,15 +91,13 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat passed = nbt.getDecimal(PASSED_ENERGY_KEY) ioLimit = nbt.mapPresent(IO_LIMIT_KEY, Decimal.Companion::deserializeNBT) - nbt.map(POWER_HISTORY_POINTER_KEY) { it: IntTag -> - historyTick = it.asInt - } - - Arrays.fill(history, Decimal.ZERO) - - for ((i, bytes) in nbt.getByteArrayList(POWER_HISTORY_KEY).withIndex()) { - history[i] = Decimal.deserializeNBT(bytes) - } + nbt.map("history5s") { it: CompoundTag -> history5s.deserializeNBT(registry, it) } + nbt.map("history15s") { it: CompoundTag -> history15s.deserializeNBT(registry, it) } + nbt.map("history1m") { it: CompoundTag -> history1m.deserializeNBT(registry, it) } + nbt.map("history10m") { it: CompoundTag -> history10m.deserializeNBT(registry, it) } + nbt.map("history1h") { it: CompoundTag -> history1h.deserializeNBT(registry, it) } + nbt.map("history6h") { it: CompoundTag -> history6h.deserializeNBT(registry, it) } + nbt.map("history24h") { it: CompoundTag -> history24h.deserializeNBT(registry, it) } } override val defaultDisplayName: Component @@ -153,7 +135,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat if (!simulate) { passed += diff - history[historyTick] += diff + thisTick += diff } return diff @@ -181,7 +163,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat if (!simulate) { passed += diff - history[historyTick] += diff + thisTick += diff } return diff @@ -286,22 +268,21 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat override fun tick() { super.tick() - lastTick = history[historyTick] - historyTick = (historyTick + 1) % history.size - history[historyTick] = Decimal.ZERO + lastTick = thisTick + graphs.forEach { it.add(thisTick) } + thisTick = Decimal.ZERO } fun clientTick() { - historyTick = (historyTick + 1) % history.size - history[historyTick] = lastTick - passed += lastTick + // TODO + // historyTick = (historyTick + 1) % history.size + // history[historyTick] = lastTick + // passed += lastTick } companion object { private val MACHINE_NAME = TranslatableComponent("block.overdrive_that_matters.energy_counter") const val PASSED_ENERGY_KEY = "passedEnergy" const val IO_LIMIT_KEY = "IOLimit" - const val POWER_HISTORY_KEY = "passHistory" - const val POWER_HISTORY_POINTER_KEY = "passHistoryPointer" } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt index 9f2bb8188..7edd77298 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/DynamicBufferSource.kt @@ -90,9 +90,6 @@ class DynamicBufferSource( init { if ( - type.name == "neoforge_text" || - type.name == "text_intensity" || - type.name == "neoforge_text_see_through" || type.name.contains("font_") || type.name.contains("text_") || type.name.contains("_text") || diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRenderer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt similarity index 85% rename from src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRenderer.kt rename to src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt index 0afbfc0a0..e34f35716 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt @@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.client.render import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.VertexSorting import net.minecraft.client.gui.Font -import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.renderer.MultiBufferSource import net.minecraft.network.chat.Component import net.minecraft.util.FormattedCharSequence @@ -279,6 +278,59 @@ fun Font.draw( ) } +data class TextIcon( + val font: Font, + val text: Component, + val scale: Float = 1f, + val color: RGBAColor = RGBAColor.WHITE, + val drawShadow: Boolean = false, + val displayMode: Font.DisplayMode = Font.DisplayMode.NORMAL, + val packedLightCoords: Int = 15728880, + val effectColor: Int = 0, + val shadowColor: RGBAColor = RGBAColor.BLACK, + val shadowX: Float = 1f, + val shadowY: Float = 1f, + val shadowZ: Float = 0.1f, + val customShadow: Boolean = false, + val drawOutline: Boolean = false, + val outlineColor: RGBAColor = RGBAColor.BLACK, + val outlineZ: Float = 0.1f, +) : IGUIRenderable { + override val width: Float = font.width(text) * scale + override val height: Float = font.lineHeight * scale + + override fun render( + guiGraphics: MGUIGraphics, + x: Float, + y: Float, + width: Float, + height: Float, + winding: UVWindingOrder, + color: RGBAColor + ) { + font.draw( + guiGraphics.pose, + text, + RenderGravity.CENTER_CENTER.x(x + width / 2f, this.width), + RenderGravity.CENTER_CENTER.y(y + height / 2f, this.height), + scale = scale, + gravity = RenderGravity.TOP_LEFT, + color = this.color * color, + drawShadow = drawShadow, + displayMode = displayMode, + packedLightCoords = packedLightCoords, + effectColor = effectColor, + shadowColor = shadowColor, + shadowX = shadowX, + shadowY = shadowY, + shadowZ = shadowZ, + customShadow = customShadow, + drawOutline = drawOutline, + outlineColor = outlineColor, + outlineZ = outlineZ,) + } +} + fun Font.draw( poseStack: PoseStack, text: String, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/GraphRendering.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/GraphRendering.kt new file mode 100644 index 000000000..d8ef7fe50 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/GraphRendering.kt @@ -0,0 +1,107 @@ +package ru.dbotthepony.mc.otm.client.render + +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.BufferUploader +import com.mojang.blaze3d.vertex.DefaultVertexFormat +import com.mojang.blaze3d.vertex.PoseStack +import com.mojang.blaze3d.vertex.VertexFormat +import net.minecraft.client.gui.Font +import net.minecraft.client.renderer.GameRenderer +import net.minecraft.network.chat.Component +import ru.dbotthepony.kommons.math.RGBAColor +import ru.dbotthepony.kommons.math.linearInterpolation +import kotlin.math.PI +import kotlin.math.acos +import kotlin.math.cos +import kotlin.math.pow +import kotlin.math.sign +import kotlin.math.sin + +private const val LINE_WIDTH = 1f +private const val HIGHLIGHT_WIDTH = 3f + +private val TEXT_BACKGROUND = RGBAColor(0f, 0f, 0f, 0.5f) + +data class GraphMouseLabels( + val mouseX: Float, + val mouseY: Float, + val labels: (Int) -> Component, + val font: Font, + val color: RGBAColor = RGBAColor.WHITE, + val pillarColor: RGBAColor = RGBAColor.WHITE, + val textColor: RGBAColor = RGBAColor.WHITE, + val textBackgroundColor: RGBAColor = TEXT_BACKGROUND +) + +fun renderGraph( + poseStack: PoseStack, + normalized: FloatArray, + width: Float, + height: Float, + color: RGBAColor = RGBAColor.WHITE, + labels: GraphMouseLabels? = null, + x: Float = 0f, + y: Float = 0f, +) { + val builder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION) + val step = width / normalized.size + + val pose = poseStack.last().pose + val checkLabels = labels != null && labels.mouseY in 0f .. height && labels.mouseX in 0f .. width + + var drawLabel = -1 + var drawPointX = 0f + var drawPointY = 0f + + for (i in 0 until normalized.size - 1) { + val y0 = y + (1f - normalized[i]) * height + val y1 = y + (1f - normalized[i + 1]) * height + + val x0 = x + width - (i + 0.5f) * step + val x1 = x + width - (i + 1.5f) * step + + val xDiff = x1 - x0 + val yDiff = y1 - y0 + + val length = (xDiff.pow(2f) + yDiff.pow(2f)).pow(0.5f) + val angle = acos(xDiff / length) * yDiff.sign - PI / 2f // rotate 90 deg + + val xDisp = cos(angle).toFloat() * LINE_WIDTH / 2f + val yDisp = sin(angle).toFloat() * LINE_WIDTH / 2f + + builder.vertex(pose, x1 - xDisp, y1 + yDisp, 0f) + builder.vertex(pose, x1 + xDisp, y1 - yDisp, 0f) + builder.vertex(pose, x0 + xDisp, y0 - yDisp, 0f) + builder.vertex(pose, x0 - xDisp, y0 + yDisp, 0f) + + //graphics.renderRect(x0, y0, LINE_WIDTH, LINE_WIDTH) + + if (checkLabels && drawLabel == -1 && labels!!.mouseX in x1 .. x0) { + drawLabel = i + drawPointX = x0 // linearInterpolation(0.5f, x0, x1) + drawPointY = y0 // linearInterpolation(0.5f, y0, y1) + } + } + + RenderSystem.setShader(GameRenderer::getPositionShader) + RenderSystem.setShaderColor(color.red, color.green, color.blue, color.alpha) + RenderSystem.disableDepthTest() + RenderSystem.disableBlend() + + BufferUploader.drawWithShader(builder.buildOrThrow()) + + if (drawLabel != -1) { + renderRect(pose, drawPointX - LINE_WIDTH / 2f, y, LINE_WIDTH, height, color = labels!!.pillarColor) + renderRect(pose, drawPointX - HIGHLIGHT_WIDTH / 2f, drawPointY - HIGHLIGHT_WIDTH / 2f, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, color = labels.color) + + val label = labels.labels(drawLabel) + val fWidth = labels.font.width(label).toFloat() + 2f + val fHeight = labels.font.lineHeight.toFloat() + 2f + + val anchorX = labels.mouseX // drawPointX + val anchorY = labels.mouseY - fHeight / 2f - 2f + + renderRect(pose, anchorX - fWidth / 2f, anchorY - fHeight / 2f, fWidth, fHeight, color = labels.textBackgroundColor) + labels.font.draw(poseStack, label, anchorX, anchorY, gravity = RenderGravity.CENTER_CENTER, color = labels.textColor) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/IGUIRenderable.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/IGUIRenderable.kt index 7b235d267..75c9eac0c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/IGUIRenderable.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/IGUIRenderable.kt @@ -31,7 +31,7 @@ interface IGUIRenderable { } /** - * Render at specified position [x], [y] with size of [width] x [height], optionally with UV [winding], if we are rendering flat texture/sprite + * Render at specified position [x], [y] with **canvas** size of [width] x [height], optionally with UV [winding], if we are rendering flat texture/sprite */ fun render( guiGraphics: MGUIGraphics, 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 1835e22a0..4454efe37 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 @@ -62,6 +62,8 @@ object Widgets18 { val FORWARD_SLASH = miscGrid.next() val RETURN_ARROW_LEFT = miscGrid.next() val CURIOS_INVENTORY = miscGrid.next() + val STATISTICS_TAB = miscGrid.next() + val SETTINGS_TAB = miscGrid.next() private val slotBgGrid = WidgetLocation.SLOT_BACKGROUNDS.grid(4, 4) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt index f33411fd2..f0c59e25c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/EnergyCounterRenderer.kt @@ -57,7 +57,7 @@ class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Con poseStack.pushPose() poseStack.translate(-0.1, -0.1, -0.1) font.draw(poseStack, buffer = sorse, text = tile.lastTick.toString(0), gravity = RenderGravity.CENTER_RIGHT, x = finalX, y = y, color = RGBAColor.WHITE) - font.draw(poseStack, buffer = sorse, text = tile.sumHistory(20).toString(0), gravity = RenderGravity.CENTER_RIGHT, x = finalX, y = y + font.lineHeight, color = RGBAColor.WHITE) + font.draw(poseStack, buffer = sorse, text = tile.history5s.calculateSum(20).toString(0), gravity = RenderGravity.CENTER_RIGHT, x = finalX, y = y + font.lineHeight, color = RGBAColor.WHITE) poseStack.popPose() y += font.lineHeight * 3 diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt index 991d78024..b61decaf9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt @@ -23,8 +23,6 @@ import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel import ru.dbotthepony.mc.otm.client.setMousePos import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory -import ru.dbotthepony.mc.otm.compat.curios.isCuriosLoaded -import ru.dbotthepony.mc.otm.compat.curios.openCuriosScreen import ru.dbotthepony.mc.otm.core.math.integerDivisionDown import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu import ru.dbotthepony.mc.otm.network.ExopackMenuOpen @@ -151,7 +149,7 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen, G : AbstractHistoryGraph, V : Any>( + screen: S, + parent: EditablePanel<*>, + val graph: G, + x: Float = 0f, + y: Float = 0f, + width: Float = 10f, + height: Float = 10f +) : EditablePanel(screen, parent, x, y, width, height) { + data class GraphData(val maximum: V) + + protected abstract fun determineGraphData(): GraphData + protected abstract fun ratio(value: V, maximum: V): Float +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryGraphPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryGraphPanel.kt new file mode 100644 index 000000000..a5a8c1e5b --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryGraphPanel.kt @@ -0,0 +1,54 @@ +package ru.dbotthepony.mc.otm.client.screen.panels + +import net.minecraft.network.chat.Component +import ru.dbotthepony.kommons.math.RGBAColor +import ru.dbotthepony.mc.otm.client.render.GraphMouseLabels +import ru.dbotthepony.mc.otm.client.render.MGUIGraphics +import ru.dbotthepony.mc.otm.client.render.renderGraph +import ru.dbotthepony.mc.otm.client.screen.MatteryScreen +import ru.dbotthepony.mc.otm.core.DecimalHistoryGraph +import ru.dbotthepony.mc.otm.core.TextComponent +import ru.dbotthepony.mc.otm.core.math.Decimal + +open class DecimalHistoryGraphPanel>( + screen: S, + parent: EditablePanel<*>, + val graph: DecimalHistoryGraph, + val formatText: (Decimal) -> Component = { TextComponent(it.toString(2)) }, + x: Float = 0f, + y: Float = 0f, + width: Float = 10f, + height: Float = 10f +) : EditablePanel(screen, parent, x, y, width, height) { + override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { + val maximum = graph.max() + val normalized: FloatArray + + if (maximum.isZero || maximum.isInfinite) { + normalized = FloatArray(graph.width) { + if (graph[it].isInfinite) 0.8f else 0.0f + } + } else { + normalized = FloatArray(graph.width) { (graph[it] / maximum).toFloat() * 0.9f } + } + + graphics.renderRect(0f, 0f, this.width, this.height, color = RGBAColor.BLACK) + + renderGraph( + graphics.pose, + normalized, + width, + height, + labels = GraphMouseLabels( + mouseX - absoluteX, + mouseY - absoluteY, + { formatText(graph[it]) }, + font, + ) + ) + } + + companion object { + + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FramePanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FramePanel.kt index 74ffc9ace..087f79782 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FramePanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/FramePanel.kt @@ -27,30 +27,10 @@ open class FramePanel( ) : EditablePanel(screen, parent, x, y, width, height), NarratableEntry { constructor(screen: S, width: Float, height: Float, title: Component? = null) : this(screen, null, 0f, 0f, width, height, title) - open inner class Tab( - var onOpen: Runnable? = null, - var onClose: Runnable? = null, + abstract inner class AbstractTab( var activeIcon: IGUIRenderable? = null, var inactiveIcon: IGUIRenderable? = activeIcon, ) : AbstractButtonPanel(this@FramePanel.screen, this@FramePanel, 0f, 0f, 26f, 28f) { - constructor(panels: List>, activeIcon: IGUIRenderable? = null, inactiveIcon: IGUIRenderable? = activeIcon) : this(activeIcon = activeIcon, inactiveIcon = inactiveIcon) { - onOpen = Runnable { - for (panel in panels) { - panel.visible = true - } - } - - onClose = Runnable { - for (panel in panels) { - panel.visible = false - } - } - - if (!isActive) { - onClose!!.run() - } - } - var isActive = tabs.isEmpty() protected set @@ -58,24 +38,6 @@ open class FramePanel( tabs.add(this) } - fun showHidePanels(input: List>) { - onOpen = Runnable { - for (child in input) { - child.visible = true - } - } - - onClose = Runnable { - for (child in input) { - child.visible = false - } - } - } - - protected fun tabIndex(): Int { - return tabs.indexOf(this) - } - override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { if (isActive) { if (tabIndex() == 0) { @@ -128,12 +90,16 @@ open class FramePanel( } } - protected open fun onOpen() { - onOpen?.run() + protected fun tabIndex(): Int { + return tabs.indexOf(this) } - protected open fun onClose() { - onClose?.run() + protected abstract fun onOpen() + protected abstract fun onClose() + + override fun onRemoved() { + super.onRemoved() + tabs.remove(this) } fun activate() { @@ -152,13 +118,77 @@ open class FramePanel( } } - override fun onClick(mouseButton: Int) { - activate() + final override fun onClick(mouseButton: Int) { + if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) { + activate() + } + } + } + + inner class CustomTab( + var onOpen: Runnable? = null, + var onClose: Runnable? = null, + activeIcon: IGUIRenderable? = null, + inactiveIcon: IGUIRenderable? = activeIcon, + ) : AbstractTab(activeIcon, inactiveIcon) { + constructor(panels: List>, activeIcon: IGUIRenderable? = null, inactiveIcon: IGUIRenderable? = activeIcon) : this(activeIcon = activeIcon, inactiveIcon = inactiveIcon) { + onOpen = Runnable { + for (panel in panels) { + panel.visible = true + } + } + + onClose = Runnable { + for (panel in panels) { + panel.visible = false + } + } + + if (!isActive) { + onClose!!.run() + } } - override fun onRemoved() { - super.onRemoved() - tabs.remove(this) + fun showHidePanels(input: List>) { + onOpen = Runnable { + for (child in input) { + child.visible = true + } + } + + onClose = Runnable { + for (child in input) { + child.visible = false + } + } + } + + override fun onOpen() { + onOpen?.run() + } + + override fun onClose() { + onClose?.run() + } + } + + inner class Tab( + activeIcon: IGUIRenderable? = null, + inactiveIcon: IGUIRenderable? = activeIcon, + ) : AbstractTab(activeIcon, inactiveIcon) { + val canvas = EditablePanel(screen, this@FramePanel) + + init { + canvas.visible = isActive + canvas.dock = Dock.FILL + } + + override fun onOpen() { + canvas.visible = true + } + + override fun onClose() { + canvas.visible = false } } @@ -238,7 +268,7 @@ open class FramePanel( } } - protected val tabs: ArrayList.Tab> = ArrayList() + protected val tabs: ArrayList.AbstractTab> = ArrayList() override fun performLayout() { for ((i, tab) in tabs.withIndex()) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/DriveViewerScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/DriveViewerScreen.kt index 3f86beea7..636b45724 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/DriveViewerScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/storage/DriveViewerScreen.kt @@ -76,8 +76,8 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp CheckBoxLabelInputPanel(this, it, menu.matchComponents, TranslatableComponent("otm.gui.filter.match_nbt")).also { it.dockTop = 4f; it.dock = Dock.TOP } }) - frame.Tab(view, activeIcon = ItemStackIcon(ItemStack(MItems.PORTABLE_CONDENSATION_DRIVE))) - frame.Tab(settings, activeIcon = ItemStackIcon(ItemStack(Items.HOPPER))) + frame.CustomTab(view, activeIcon = ItemStackIcon(ItemStack(MItems.PORTABLE_CONDENSATION_DRIVE))) + frame.CustomTab(settings, activeIcon = ItemStackIcon(ItemStack(Items.HOPPER))) return frame } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EnergyCounterScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EnergyCounterScreen.kt index 838d9e2a6..e4d35f022 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EnergyCounterScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/EnergyCounterScreen.kt @@ -2,13 +2,18 @@ package ru.dbotthepony.mc.otm.client.screen.tech import net.minecraft.network.chat.Component import net.minecraft.world.entity.player.Inventory +import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.mc.otm.client.ShiftPressedCond +import ru.dbotthepony.mc.otm.client.render.IGUIRenderable +import ru.dbotthepony.mc.otm.client.render.TextIcon +import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.client.screen.panels.* import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls import ru.dbotthepony.mc.otm.client.screen.panels.input.NetworkNumberInputPanel +import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.util.formatPower import ru.dbotthepony.mc.otm.menu.tech.EnergyCounterMenu @@ -17,19 +22,37 @@ class EnergyCounterScreen(menu: EnergyCounterMenu, inventory: Inventory, title: val frame = super.makeMainFrame()!! frame.height = 160f - frame.width += 40f + frame.width += 60f + + val graphs = linkedMapOf( + menu.history5s to "5s", + menu.history15s to "15s", + menu.history1m to "1m", + menu.history1h to "1h", + menu.history6h to "6h", + menu.history24h to "24h", + ) + + for ((graph, text) in graphs) { + val tab = frame.Tab(activeIcon = TextIcon(color = RGBAColor.BLACK, font = font, text = TextComponent(text))) + val panel = DecimalHistoryGraphPanel(this, tab.canvas, graph, formatText = { it.formatPower(formatAsReadable = ShiftPressedCond) }) + panel.dock = Dock.FILL + } + + val informationTab = frame.Tab(activeIcon = Widgets18.STATISTICS_TAB) + val limitsTab = frame.Tab(activeIcon = Widgets18.SETTINGS_TAB) val labels = ArrayList>() - labels.add(Label(this, frame, text = TranslatableComponent("otm.gui.power.passed"))) - labels.add(DynamicLabel(this, frame, textSupplier = { menu.passed.formatPower(formatAsReadable = ShiftPressedCond) })) - labels.add(Label(this, frame, text = TranslatableComponent("otm.gui.power.average"))) - labels.add(DynamicLabel(this, frame, textSupplier = { menu.average.formatPower(formatAsReadable = ShiftPressedCond) })) - labels.add(Label(this, frame, text = TranslatableComponent("otm.gui.power.last_20_ticks"))) - labels.add(DynamicLabel(this, frame, textSupplier = { menu.last20Ticks.formatPower(formatAsReadable = ShiftPressedCond) })) - labels.add(Label(this, frame, text = TranslatableComponent("otm.gui.power.last_tick"))) - labels.add(DynamicLabel(this, frame, textSupplier = { menu.lastTick.formatPower(formatAsReadable = ShiftPressedCond) })) - labels.add(DynamicLabel(this, frame, textSupplier = { TranslatableComponent("block.overdrive_that_matters.energy_counter.facing", menu.inputDirection) })) + labels.add(Label(this, informationTab.canvas, text = TranslatableComponent("otm.gui.power.passed"))) + labels.add(DynamicLabel(this, informationTab.canvas, textSupplier = { menu.passed.formatPower(formatAsReadable = ShiftPressedCond) })) + labels.add(Label(this, informationTab.canvas, text = TranslatableComponent("otm.gui.power.average"))) + labels.add(DynamicLabel(this, informationTab.canvas, textSupplier = { menu.history5s.calculateAverage(20).formatPower(formatAsReadable = ShiftPressedCond) })) + labels.add(Label(this, informationTab.canvas, text = TranslatableComponent("otm.gui.power.last_20_ticks"))) + labels.add(DynamicLabel(this, informationTab.canvas, textSupplier = { menu.history5s.calculateSum(20).formatPower(formatAsReadable = ShiftPressedCond) })) + labels.add(Label(this, informationTab.canvas, text = TranslatableComponent("otm.gui.power.last_tick"))) + labels.add(DynamicLabel(this, informationTab.canvas, textSupplier = { menu.lastTick.formatPower(formatAsReadable = ShiftPressedCond) })) + labels.add(DynamicLabel(this, informationTab.canvas, textSupplier = { TranslatableComponent("block.overdrive_that_matters.energy_counter.facing", menu.inputDirection) })) for ((i, label) in labels.withIndex()) { if (i == 0) { @@ -42,33 +65,21 @@ class EnergyCounterScreen(menu: EnergyCounterMenu, inventory: Inventory, title: } if (!menu.player.isSpectator) { - val button = ButtonPanel(this, frame, 0f, 0f, 0f, 20f, TranslatableComponent("block.overdrive_that_matters.energy_counter.switch"), onPress = Runnable { menu.switchDirection.accept(null) }) + val button = ButtonPanel(this, informationTab.canvas, 0f, 0f, 0f, 20f, TranslatableComponent("block.overdrive_that_matters.energy_counter.switch"), onPress = Runnable { menu.switchDirection.accept(null) }) button.dock = Dock.TOP button.setDockMargin(4f, 5f, 4f, 0f) } - val infoPanels = frame.fetchChildren() - - Label(this, frame, TranslatableComponent("block.overdrive_that_matters.energy_counter.limit")).also { + Label(this, limitsTab.canvas, TranslatableComponent("block.overdrive_that_matters.energy_counter.limit")).also { it.dock = Dock.TOP it.dockTop = 10f } - NetworkNumberInputPanel(this, frame, widget = menu.maxIOInput, networkValue = menu::maxIO).also { + NetworkNumberInputPanel(this, limitsTab.canvas, widget = menu.maxIOInput, networkValue = menu::maxIO).also { it.dock = Dock.TOP it.dockTop = 4f } - val limitsPanels = frame.fetchChildren().filter { !infoPanels.contains(it) } - - val informationTab = frame.Tab() - val limitsTab = frame.Tab() - - informationTab.showHidePanels(infoPanels) - limitsTab.showHidePanels(limitsPanels) - - limitsTab.onClose!!.run() - makeDeviceControls(this, frame, redstoneConfig = menu.redstone) return frame diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/AbstractHistoryGraph.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/AbstractHistoryGraph.kt index 35d715784..9f65f7e77 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/AbstractHistoryGraph.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/AbstractHistoryGraph.kt @@ -136,10 +136,6 @@ abstract class AbstractHistoryGraph( private val accumulator = ArrayList(this.resolution) private val values = ArrayDeque() - init { - values.fill(identity()) - } - /** * Calculates sum of values provided by calls to [add] and writes it to graph history or graph value accumulator */ diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/DecimalHistoryGraph.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/DecimalHistoryGraph.kt index 55e511684..defb5fb69 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/DecimalHistoryGraph.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/DecimalHistoryGraph.kt @@ -7,7 +7,6 @@ import ru.dbotthepony.mc.otm.core.collect.reduce import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.data.DecimalCodec import ru.dbotthepony.mc.otm.network.MatteryStreamCodec -import kotlin.math.pow class DecimalHistoryGraph : AbstractHistoryGraph { constructor(resolution: Int, width: Int) : super(resolution, width) @@ -17,6 +16,10 @@ class DecimalHistoryGraph : AbstractHistoryGraph { return input.iterator().reduce(Decimal.ZERO, Decimal::plus) / Decimal(input.size) } + override fun sum(input: List): Decimal { + return input.iterator().reduce(Decimal.ZERO, Decimal::plus) + } + override fun sum(a: Decimal, b: Decimal): Decimal { return a + b } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/EnergyCounterMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/EnergyCounterMenu.kt index acfec93a4..bff72762d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/EnergyCounterMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/tech/EnergyCounterMenu.kt @@ -9,6 +9,7 @@ import ru.dbotthepony.kommons.util.setValue import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting import ru.dbotthepony.mc.otm.block.tech.EnergyCounterBlock import ru.dbotthepony.mc.otm.block.entity.tech.EnergyCounterBlockEntity +import ru.dbotthepony.mc.otm.core.DecimalHistoryGraph import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.toDecimal import ru.dbotthepony.mc.otm.menu.MatteryMenu @@ -22,11 +23,27 @@ class EnergyCounterMenu( tile: EnergyCounterBlockEntity? = null ) : MatteryMenu(MMenus.ENERGY_COUNTER, p_38852_, inventory, tile) { var passed by mSynchronizer.decimal() - var average by mSynchronizer.decimal() - var last20Ticks by mSynchronizer.decimal() var lastTick by mSynchronizer.decimal() var maxIO by mSynchronizer.decimal() + val history5s = tile?.history5s ?: DecimalHistoryGraph(1, 100) + val history15s = tile?.history15s ?: DecimalHistoryGraph(3, 100) + val history1m = tile?.history1m ?: DecimalHistoryGraph(12, 100) + val history10m = tile?.history10m ?: DecimalHistoryGraph(120, 100) + val history1h = tile?.history1h ?: DecimalHistoryGraph(720, 100) + val history6h = tile?.history6h ?: DecimalHistoryGraph(720 * 6, 100) + val history24h = tile?.history24h ?: DecimalHistoryGraph(720 * 24, 100) + + init { + mSynchronizer.add(history5s) + mSynchronizer.add(history15s) + mSynchronizer.add(history1m) + mSynchronizer.add(history10m) + mSynchronizer.add(history1h) + mSynchronizer.add(history6h) + mSynchronizer.add(history24h) + } + val switchDirection = oneWayInput { if (tile is EnergyCounterBlockEntity) tile.level?.setBlock(tile.blockPos, tile.blockState.setValue(EnergyCounterBlock.INPUT_DIRECTION, tile.blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION).opposite), Block.UPDATE_ALL) @@ -52,22 +69,12 @@ class EnergyCounterMenu( } } - // TODO: Graph and proper networking for it - private var ticksPassed = 0 - override fun beforeBroadcast() { super.beforeBroadcast() if (tile is EnergyCounterBlockEntity) { passed = tile.passed - average = tile.calcAverage(20) lastTick = tile.lastTick - - if (ticksPassed == 0) { - last20Ticks = tile.sumHistory(20) - } - - ticksPassed = (ticksPassed + 1) % 20 inputDirection = tile.blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION) maxIO = tile.ioLimit?.toDecimal() ?: -Decimal.ONE diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/misc18.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/widgets/misc18.png index e8e91ae609122f954b900a2c7067d82e53a4673a..d37acf339dcd7ec9189a888138bbddeb1e2f3f18 100644 GIT binary patch delta 866 zcmV-o1D*W32Db;0K@VG{D%A>B5OK&*o$R6_j*(C$e|B(EbdeJOmlRsWcyQd0clRE5 z?*MP3!W5$`3Mi_nr{Xaon_Ct9ujs`v1`))7$P6u!Ud+ICeBHyt*SiSMazFRy=$G>b z13Ut8lxez2yg@v>X=t?9!*866HhXzyNYA9^i9^Ifv4f=! zW+h!Af1V)@%Zf_*-mJqi=Pk}krN&zKz4I>Y+20{ z!1oMXscnC?0nB}pUTCIJ@&EtAPt_IklO(#hy`y>=paw;V3d ziHNl3m18NTRGV+TSC5@2j+w^t#aJRBpOatfwX)6P|r z9;#v^j_)8+8GUM*pV0?-9NsmO!m60p(I@7lEk1XwzCQr`6zTl+&hws+>!ddle{{y2 zu}-?y+teBow||HdaSJpP6HBf!;5rcz2%KBZ+&;>i_@% delta 740 zcmV(9g$Eae|2$EbdeJOmlRsWcyQd0clRE5 z?*O4$WvbaT2dJ85q!V#5mtPeFuLxie!-yd&F;h=w7PIgiU-$6w^)AA*ywCkP29$!y z0G~)a$8^IY-XNacv~SDQz zS=msDe`kopimFk*kaJn#yv13q)>-?W{Dr}SwvypG&2c2Kgd|cBA)}58s<05DRU^ej zn)c%!{xQd&B9~0A3K%&SP=gA|@q_=t?{3XvblOb{#(~}!+y0mU0=qz~VcXxww%s}b z{LjFZ*74VxK4ux>~*g4i16I5@oM@yt}Wvw|~#H`}+YW(sJ+9Lph%S000JJOGiWi00000 z0Qp0^e*gdg32;bRa{vGf6951U69E94oEQKA00(qQO+^Ri2M!YeG^)PCB>(^cH%UZ6 ze^hwd?b^`}!XONV;X!==SH>Gn2m#tcVMG7#(#;BISmxq&VW0Fd`@YdSE4*3*E?)~F zgw}cGv6ND(ov;0@kvgkH&tmyxG%FxKCx5TC&h~)Ht7f^1+R3%TSsjwKR@4-+7L_I^ zg|jN8k*{p9BwXt?y-C)B(1tS-=shDQ zq^EYVnSnncq;mC%+q}npkY&J6T}jJ!G3!82>`9M!+_C%f0RV^UK4-4oNNFB%O8m5M z0KFfXf4`OHLF5RB6=Zwd0Rkc*nlw%NUd=x*NH>ZA0000000000000000001hGra+H WORucls#o3s0000|HXtQ#>{?3W5~r^Xr0t?0 z%0og_s1kw=D?o}6L209eEo{)-pTNHWL_7VynS1I<>n<=ta+{9z%{g<)du#2JD+7+`cRGWW zjo9}0c*h>>%xCsW@P%vSI*#VwcC`48qvcl}tu8rQKk4X!n${+0i;BLk_w_G&C;2o! z%3f0djcR}w$iKXa8iofb=b%E$gQyoh{#lPNdVK8hC66zAe8uCd;JMxmz>){JHs_yB zc@V4585Hm@QJ(VR82m3@$z1muVg$~MlKI(DTZ@kJ-$h0rEf&2!Sae z3q%0>6jf--Dr+i08GJF7Q_p_v&v6{jc{m^E4bpY$*rUU-Vio^-JOqYYWfOro^c$W- z>#y~8vZ=TAO}(3Zl3W3|r+4&r46+sLAN7`P?>jF4muOl9uU zI(We}_#?2$o7@6PQ}^cax2T*Xr?o+fXByXOL;j&*iI#`_T_dDU8@)F|2T^oQGr6pHWBn6y_+`Dvou8y|5KkO89I`TjqosGfwwNRhhtkI*bP$!k z75V$YYeq1KB%MS44O8l_D8qIRIXMeGuP?l=WWbXKJOeCcn=qF0DkFXVf(*Ax+n$2l z>F&M$xcO!M89t=^BWAg$??0g9_L=owf54uTw#R((arcpEaoF*CUom3I%c3 zaZd<-VDU=hoY#=K(@uUf2 delta 183 zcmeyZbwzH19;4($6B9;`iJ7kz8yLWVB@jZhdq8L|PYBJUx4D&(hmo;$a=x$>6Vt-U z4FY0JOglICaU?Nr)?le&WSlYiAgeGZ3q(2V79dZY@yzC%tPM6Tgg4`8gLGB5Y=W<_WVFK!&ypeIjVGyeVJ>yfvE}c^@zW0F(zT#{d8T