"Diagram" basic rendering code, added gui icons for energy counter

This commit is contained in:
DBotThePony 2024-09-22 17:51:15 +07:00
parent 6291cecb7e
commit a6ff93d74c
Signed by: DBot
GPG Key ID: DCC23B5715498507
19 changed files with 423 additions and 166 deletions

View File

@ -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<Decimal> {
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"
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Exopack
height = topLine.height, centerSprite = true)
if (menu.capability.isExopackSmeltingInstalled || menu.capability.isExopackEnderAccessInstalled) {
val craftingTab = frame.Tab()
val craftingTab = frame.CustomTab()
craftingTab.activeIcon = ItemStackIcon(ItemStack(Items.CRAFTING_TABLE))
craftingTab.inactiveIcon = craftingTab.activeIcon
@ -161,7 +159,7 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<Exopack
}
if (menu.capability.isExopackSmeltingInstalled) {
val tab = frame.Tab()
val tab = frame.CustomTab()
tab.activeIcon = ItemStackIcon(ItemStack(Items.FURNACE))
tab.inactiveIcon = tab.activeIcon
@ -205,7 +203,7 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<Exopack
}
if (menu.capability.isExopackEnderAccessInstalled) {
val tab = frame.Tab()
val tab = frame.CustomTab()
tab.activeIcon = ItemStackIcon(ItemStack(Items.ENDER_CHEST))
tab.inactiveIcon = tab.activeIcon

View File

@ -122,11 +122,11 @@ class MatterPanelScreen(
scrollBar.dock = Dock.RIGHT
frame.Tab(onOpen = { isPatternView = true; scrollBar.scroll = scrollPatterns }, activeIcon = PATTERN_LIST_ACTIVE, inactiveIcon = PATTERN_LIST_INACTIVE).also {
frame.CustomTab(onOpen = { isPatternView = true; scrollBar.scroll = scrollPatterns }, activeIcon = PATTERN_LIST_ACTIVE, inactiveIcon = PATTERN_LIST_INACTIVE).also {
it.tooltips.add(TranslatableComponent("otm.gui.matter_panel.patterns"))
}
frame.Tab(onOpen = { isPatternView = false; scrollBar.scroll = scrollTasks }, activeIcon = TASK_LIST_ACTIVE, inactiveIcon = TASK_LIST_INACTIVE).also {
frame.CustomTab(onOpen = { isPatternView = false; scrollBar.scroll = scrollTasks }, activeIcon = TASK_LIST_ACTIVE, inactiveIcon = TASK_LIST_INACTIVE).also {
it.tooltips.add(TranslatableComponent("otm.gui.matter_panel.tasks"))
}

View File

@ -0,0 +1,19 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.core.AbstractHistoryGraph
abstract class AbstractHistoryGraphPanel<out S : MatteryScreen<*>, G : AbstractHistoryGraph<V>, V : Any>(
screen: S,
parent: EditablePanel<*>,
val graph: G,
x: Float = 0f,
y: Float = 0f,
width: Float = 10f,
height: Float = 10f
) : EditablePanel<S>(screen, parent, x, y, width, height) {
data class GraphData<V : Any>(val maximum: V)
protected abstract fun determineGraphData(): GraphData<V>
protected abstract fun ratio(value: V, maximum: V): Float
}

View File

@ -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<out S : MatteryScreen<*>>(
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<S>(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 {
}
}

View File

@ -27,30 +27,10 @@ open class FramePanel<out S : Screen>(
) : EditablePanel<S>(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<S>(this@FramePanel.screen, this@FramePanel, 0f, 0f, 26f, 28f) {
constructor(panels: List<EditablePanel<*>>, 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<out S : Screen>(
tabs.add(this)
}
fun showHidePanels(input: List<EditablePanel<*>>) {
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<out S : Screen>(
}
}
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<out S : Screen>(
}
}
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<EditablePanel<*>>, 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<EditablePanel<*>>) {
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<out S : Screen>(
}
}
protected val tabs: ArrayList<FramePanel<*>.Tab> = ArrayList()
protected val tabs: ArrayList<FramePanel<*>.AbstractTab> = ArrayList()
override fun performLayout() {
for ((i, tab) in tabs.withIndex()) {

View File

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

View File

@ -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<EditablePanel<*>>()
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

View File

@ -136,10 +136,6 @@ abstract class AbstractHistoryGraph<V : Any>(
private val accumulator = ArrayList<V>(this.resolution)
private val values = ArrayDeque<V>()
init {
values.fill(identity())
}
/**
* Calculates sum of values provided by calls to [add] and writes it to graph history or graph value accumulator
*/

View File

@ -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<Decimal> {
constructor(resolution: Int, width: Int) : super(resolution, width)
@ -17,6 +16,10 @@ class DecimalHistoryGraph : AbstractHistoryGraph<Decimal> {
return input.iterator().reduce(Decimal.ZERO, Decimal::plus) / Decimal(input.size)
}
override fun sum(input: List<Decimal>): Decimal {
return input.iterator().reduce(Decimal.ZERO, Decimal::plus)
}
override fun sum(a: Decimal, b: Decimal): Decimal {
return a + b
}

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 826 B

After

Width:  |  Height:  |  Size: 951 B