diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt index 1a5e04757..763d8ccff 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt @@ -541,6 +541,7 @@ private fun blocks(provider: MatteryLanguageProvider) { add(MBlocks.ENERGY_COUNTER[null]!!, "facing", "Input facing: %s") add(MBlocks.ENERGY_COUNTER[null]!!, "switch", "Switch input facing") add(MBlocks.ENERGY_COUNTER[null]!!, "limit", "I/O Limit. -1 means no limit") + add(MBlocks.ENERGY_COUNTER[null]!!, "display_this", "Display this information on block's screen") addBlock(MBlocks.CHEMICAL_GENERATOR.values, "Chemical Generator") addBlock(MBlocks.CHEMICAL_GENERATOR.values, "desc", "Generates power by burning solid fuels") @@ -840,6 +841,7 @@ private fun gui(provider: MatteryLanguageProvider) { gui("time.short.5s", "5s") gui("time.short.15s", "15s") gui("time.short.1m", "1m") + gui("time.short.10m", "10m") gui("time.short.1h", "1h") gui("time.short.6h", "6h") gui("time.short.24h", "24h") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt index f49758940..f8575df20 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt @@ -549,6 +549,7 @@ private fun blocks(provider: MatteryLanguageProvider) { add(MBlocks.ENERGY_COUNTER[null]!!, "facing", "Сторона входа: %s") add(MBlocks.ENERGY_COUNTER[null]!!, "switch", "Сменить сторону входа") add(MBlocks.ENERGY_COUNTER[null]!!, "limit", "Лимит ввода/вывода. -1 для отключения лимитов") + add(MBlocks.ENERGY_COUNTER[null]!!, "display_this", "Отображать эти данные на экране блока") addBlock(MBlocks.CHEMICAL_GENERATOR.values, "Химический генератор") addBlock(MBlocks.CHEMICAL_GENERATOR.values, "desc", "Генерирует энергию сжигая твёрдое топливо") @@ -848,6 +849,7 @@ private fun gui(provider: MatteryLanguageProvider) { gui("time.short.5s", "5с") gui("time.short.15s", "15с") gui("time.short.1m", "1м") + gui("time.short.10m", "10м") gui("time.short.1h", "1ч") gui("time.short.6h", "6ч") gui("time.short.24h", "24ч") 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 a40bf22ac..e6eb2170f 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 @@ -10,6 +10,7 @@ import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.level.block.state.BlockState import net.neoforged.neoforge.capabilities.Capabilities +import ru.dbotthepony.kommons.util.DelegateSetter import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.setValue import ru.dbotthepony.mc.otm.block.tech.EnergyCounterBlock @@ -21,7 +22,6 @@ import ru.dbotthepony.mc.otm.core.math.BlockRotation import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.getDecimal -import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.mapPresent import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.util.countingLazy @@ -43,22 +43,24 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat val history6h = DecimalHistoryChart(720 * 6, 100) val history24h = DecimalHistoryChart(720 * 24, 100) - private val graphs = ImmutableList.of( + val charts: ImmutableList = ImmutableList.of( history5s, history15s, history1m, history10m, history1h, history6h, history24h, ) init { - syncher.add(history5s) + charts.forEach { syncher.add(it) } } private var thisTick = Decimal.ZERO var lastTick by syncher.decimal() internal set + var displayChartOnBlock by syncher.int(0, setter = DelegateSetter { field, value -> if (value in 0 .. charts.size + 1) field.accept(value) }) + var ioLimit: Decimal? = null fun resetStats() { - graphs.forEach { it.clear() } + charts.forEach { it.clear() } lastTick = Decimal.ZERO passed = Decimal.ZERO } @@ -69,16 +71,19 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat ioLimit?.let { nbt[IO_LIMIT_KEY] = it.serializeNBT() } } + init { + savetables.int(::displayChartOnBlock) + savetables.stateful(::history5s) + savetables.stateful(::history15s) + savetables.stateful(::history1m) + savetables.stateful(::history10m) + savetables.stateful(::history1h) + savetables.stateful(::history6h) + savetables.stateful(::history24h) + } + override fun saveLevel(nbt: CompoundTag, registry: HolderLookup.Provider) { super.saveLevel(nbt, registry) - - 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) { @@ -86,14 +91,6 @@ 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("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 @@ -265,7 +262,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat super.tick() lastTick = thisTick - graphs.forEach { it.add(thisTick) } + charts.forEach { it.add(thisTick) } thisTick = Decimal.ZERO } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ChartRendering.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ChartRendering.kt index a1d5fcef7..facdfa1c0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ChartRendering.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ChartRendering.kt @@ -1,16 +1,18 @@ package ru.dbotthepony.mc.otm.client.render import com.mojang.blaze3d.systems.RenderSystem -import com.mojang.blaze3d.vertex.BufferBuilder -import com.mojang.blaze3d.vertex.BufferUploader import com.mojang.blaze3d.vertex.ByteBufferBuilder import com.mojang.blaze3d.vertex.DefaultVertexFormat import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.VertexFormat +import com.mojang.blaze3d.vertex.VertexSorting import net.minecraft.client.gui.Font import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent import net.minecraft.client.renderer.GameRenderer +import net.minecraft.client.renderer.MultiBufferSource +import net.minecraft.client.renderer.RenderStateShard +import net.minecraft.client.renderer.RenderType import net.minecraft.network.chat.Component import net.minecraft.world.inventory.tooltip.TooltipComponent import net.neoforged.neoforge.client.event.RegisterClientTooltipComponentFactoriesEvent @@ -68,7 +70,10 @@ class ChartTooltipElement( } override fun renderImage(font: Font, x: Int, y: Int, graphics: GuiGraphics) { - renderChart(graphics.pose(), normalized, this.width, this.height - 10f, levelLabels = this.levelLabels, x = x.toFloat(), y = y.toFloat() + 10f) + graphics.pose().pushPose() + graphics.pose().translate(0f, 0f, 0.1f) + renderChart(graphics.pose(), normalized, this.width, this.height - 10f, levelLabels = this.levelLabels, x = x.toFloat(), y = y.toFloat() + 10f, buffer = graphics.bufferSource()) + graphics.pose().popPose() } companion object { @@ -78,6 +83,32 @@ class ChartTooltipElement( } } +val CHART_RENDER_TYPE = run { + val builder = RenderType.CompositeState.builder() + + builder.setShaderState(RenderStateShard.ShaderStateShard(GameRenderer::getPositionColorShader)) + builder.setDepthTestState(RenderStateShard.DepthTestStateShard("always", GL_LESS)) + + builder.setTransparencyState(RenderStateShard.TransparencyStateShard("normal_blend", { + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + }, { RenderSystem.disableBlend() })) + + @Suppress("INACCESSIBLE_TYPE") + RenderType.create( + "otm_chart", + DefaultVertexFormat.POSITION_COLOR, + VertexFormat.Mode.QUADS, + 16_000, + false, + true, + builder.createCompositeState(false) + ) as RenderType +} + +private val buffer = DynamicBufferSource(vertexSorting = VertexSorting.ORTHOGRAPHIC_Z) +private fun buffer() = buffer + fun renderChart( poseStack: PoseStack, charts: List>, @@ -85,16 +116,20 @@ fun renderChart( height: Float, labels: ChartMouseLabels? = null, levelLabels: ChartLevelLabels? = null, + buffer: MultiBufferSource = buffer(), x: Float = 0f, y: Float = 0f, + textScale: Float = 1f, + lineWidth: Float = LINE_WIDTH ) { require(charts.all { it.first.size == charts[0].first.size }) { "Provided chart data is not of same width" } - val builder = BufferBuilder(BYTE_BUFFER_BUILDER, VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR) + val builder = buffer.getBuffer(CHART_RENDER_TYPE) val step = width / charts[0].first.size - val pose = poseStack.last().pose val checkLabels = labels != null && labels.mouseY in 0f .. height && labels.mouseX in 0f .. width + poseStack.pushPose() + var drawLabel = -1 var drawPointX = 0f var drawPointY = 0f @@ -103,13 +138,15 @@ fun renderChart( for ((level, label) in levelLabels.labels) { val y0 = y + (1f - level) * height - builder.vertex(pose, x + width, LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor) - builder.vertex(pose, x + width, -LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor) - builder.vertex(pose, x, -LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor) - builder.vertex(pose, x, LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor) + builder.vertex(poseStack, x + width, lineWidth / 2f + y0, 0f).color(levelLabels.lineColor) + builder.vertex(poseStack, x + width, -lineWidth / 2f + y0, 0f).color(levelLabels.lineColor) + builder.vertex(poseStack, x, -lineWidth / 2f + y0, 0f).color(levelLabels.lineColor) + builder.vertex(poseStack, x, lineWidth / 2f + y0, 0f).color(levelLabels.lineColor) } } + poseStack.translate(0f, 0f, 0.2f) + for ((normalized, color) in charts) { for (i in 0 until normalized.size - 1) { val y0 = y + (1f - normalized[i]) * height @@ -122,21 +159,21 @@ fun renderChart( val yDiff = y1 - y0 if (yDiff.sign == 0f) { - builder.vertex(pose, x0, y0 + LINE_WIDTH / 2f, 0f).color(color) - builder.vertex(pose, x0, y0 - LINE_WIDTH / 2f, 0f).color(color) - builder.vertex(pose, x1, y1 - LINE_WIDTH / 2f, 0f).color(color) - builder.vertex(pose, x1, y1 + LINE_WIDTH / 2f, 0f).color(color) + builder.vertex(poseStack, x0, y0 + lineWidth / 2f, 0f).color(color) + builder.vertex(poseStack, x0, y0 - lineWidth / 2f, 0f).color(color) + builder.vertex(poseStack, x1, y1 - lineWidth / 2f, 0f).color(color) + builder.vertex(poseStack, x1, y1 + lineWidth / 2f, 0f).color(color) } else { 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 + val xDisp = cos(angle).toFloat() * lineWidth / 2f + val yDisp = sin(angle).toFloat() * lineWidth / 2f - builder.vertex(pose, x0 + xDisp, y0 + yDisp, 0f).color(color) - builder.vertex(pose, x0 - xDisp, y0 - yDisp, 0f).color(color) - builder.vertex(pose, x1 - xDisp, y1 - yDisp, 0f).color(color) - builder.vertex(pose, x1 + xDisp, y1 + yDisp, 0f).color(color) + builder.vertex(poseStack, x0 + xDisp, y0 + yDisp, 0f).color(color) + builder.vertex(poseStack, x0 - xDisp, y0 - yDisp, 0f).color(color) + builder.vertex(poseStack, x1 - xDisp, y1 - yDisp, 0f).color(color) + builder.vertex(poseStack, x1 + xDisp, y1 + yDisp, 0f).color(color) } //graphics.renderRect(x0, y0, LINE_WIDTH, LINE_WIDTH) @@ -149,57 +186,58 @@ fun renderChart( } } - RenderSystem.setShader(GameRenderer::getPositionColorShader) - // RenderSystem.setShaderColor(color.red, color.green, color.blue, color.alpha) - RenderSystem.disableDepthTest() - RenderSystem.disableBlend() - RenderSystem.setShaderColor(1f, 1f, 1f, 1f) - - BufferUploader.drawWithShader(builder.buildOrThrow()) - - RenderSystem.enableDepthTest() - RenderSystem.depthFunc(GL_LESS) - if (levelLabels != null && levelLabels.labels.isNotEmpty()) { + poseStack.translate(0f, 0f, 0.2f) val font = levelLabels.font ?: minecraft.font + for ((level, label) in levelLabels.labels) { val y0 = y + (1f - level) * height - val tX = x + levelLabels.textGravity.repositionX(width, font.width(label).toFloat()) - 1f - val tY = y0 - LINE_WIDTH - 1f - font.lineHeight + levelLabels.textGravity.repositionY(font.lineHeight * 2f + LINE_WIDTH + 2f, font.lineHeight.toFloat()) + 1f + val tX = x + (levelLabels.textGravity.repositionX(width - 1f, font.width(label).toFloat() * textScale - 1f)) + val tY = y0 - lineWidth - 1f - font.lineHeight * textScale + levelLabels.textGravity.repositionY(font.lineHeight * 2f * textScale + lineWidth + 2f, font.lineHeight.toFloat() * textScale) + 1f font.draw( poseStack, label, + buffer = buffer, x = tX, y = tY, color = levelLabels.textColor, drawOutline = true, - outlineZ = -1f, - outlineColor = levelLabels.textOutline + outlineZ = -0.1f, + outlineColor = levelLabels.textOutline, + scale = textScale ) } } 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) + poseStack.translate(0f, 0f, 0.2f) + + uploadRect(poseStack, drawPointX - lineWidth / 2f, y, lineWidth, height, buffer = buffer, color = labels!!.pillarColor) + uploadRect(poseStack, drawPointX - HIGHLIGHT_WIDTH / 2f, drawPointY - HIGHLIGHT_WIDTH / 2f, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, buffer = buffer, color = labels.color) val label = labels.labels(drawLabel) if (label.isNotEmpty()) { - val fWidth = label.maxOf { labels.font.width(it).toFloat() + 2f } - val fHeight = labels.font.lineHeight.toFloat() * label.size + 2f + val fWidth = label.maxOf { (labels.font.width(it).toFloat() + 2f) * textScale } + val fHeight = (labels.font.lineHeight.toFloat() * label.size + 2f) * textScale val anchorX = labels.mouseX // drawPointX var anchorY = labels.mouseY - fHeight - 2f - renderRect(pose, anchorX - fWidth / 2f, anchorY - 2f, fWidth, fHeight + 4f, color = labels.textBackgroundColor) + uploadRect(poseStack, anchorX - fWidth / 2f, anchorY - 2f, fWidth, fHeight + 4f, buffer = buffer, color = labels.textBackgroundColor) label.forEach { - labels.font.draw(poseStack, it, anchorX, anchorY, gravity = RenderGravity.TOP_CENTER, color = labels.textColor) + labels.font.draw(poseStack, it, anchorX, anchorY, buffer = buffer, gravity = RenderGravity.TOP_CENTER, color = labels.textColor, scale = textScale) anchorY += labels.font.lineHeight } } } + + poseStack.popPose() + + if (buffer === buffer()) { + buffer.endBatch() + } } 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 7edd77298..b90716096 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 @@ -162,7 +162,15 @@ class DynamicBufferSource( next = State(Sheets.shulkerBoxSheet(), ImmutableList.of(next.type), immutableAfter = true, chained = false) next = State(Sheets.signSheet(), ImmutableList.of(next.type), immutableAfter = true, chained = false) //next = State(Sheets.hangingSignSheet(), ImmutableList.of(next.type), immutableAfter = true, chained = false) + next = State(Sheets.chestSheet(), ImmutableList.of(next.type), immutableAfter = true, chained = false) + next = State(CHART_RENDER_TYPE, ImmutableList.of(next.type), immutableAfter = true, chained = true) + + next = State(rectRenderType(true, false, true), ImmutableList.of(next.type), immutableAfter = true, chained = true) + next = State(rectRenderType(true, false, false), ImmutableList.of(next.type), immutableAfter = true, chained = true) + next = State(rectRenderType(false, false, true), ImmutableList.of(next.type), immutableAfter = true, chained = true) + next = State(rectRenderType(false, false, false), ImmutableList.of(next.type), immutableAfter = true, chained = true) + next = State(RenderType.armorEntityGlint(), ImmutableList.of(next.type), immutableAfter = true, chained = false) next = State(RenderType.glint(), ImmutableList.of(next.type), immutableAfter = true, chained = false) next = State(RenderType.glintTranslucent(), ImmutableList.of(next.type), immutableAfter = true, chained = false) @@ -170,6 +178,9 @@ class DynamicBufferSource( next = State(RenderType.entityGlintDirect(), ImmutableList.of(next.type), immutableAfter = true, chained = false) next = State(RenderType.waterMask(), ImmutableList.of(next.type), immutableAfter = true, chained = false) + next = State(rectRenderType(true, true, true), ImmutableList.of(next.type), immutableAfter = true, chained = true) + next = State(rectRenderType(true, true, false), ImmutableList.of(next.type), immutableAfter = true, chained = true) + ModelBakery.DESTROY_TYPES.forEach { next = State(it, ImmutableList.of(next.type), immutableAfter = true, chained = false) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt index ff9ab2abc..02e7e428d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/FontRendering.kt @@ -165,8 +165,8 @@ private fun Font.drawInternal( drawInBatch( text, - (x + shadowX) * inv, - (y + shadowY) * inv, + (x + shadowX * scale) * inv, + (y + shadowY * scale) * inv, shadowColor.toBGR(), false, poseStack.last().pose(), @@ -189,8 +189,8 @@ private fun Font.drawInternal( for ((_x, _y) in outlinePositions) { drawInBatch( text, - (x + _x) * inv, - (y + _y) * inv, + (x + _x * scale) * inv, + (y + _y * scale) * inv, outlineColor.toBGR(), drawShadow, poseStack.last().pose(), diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderExtensions.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderExtensions.kt index 1c7ad7433..fc9479891 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderExtensions.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderExtensions.kt @@ -18,6 +18,7 @@ val tesselator: Tesselator get() = Tesselator.getInstance() // what meth have you been smoking, mojang, with new names fun VertexConsumer.vertex(matrix4f: Matrix4f, x: Float, y: Float, z: Float): VertexConsumer = addVertex(matrix4f, x, y, z) +fun VertexConsumer.vertex(poseStack: PoseStack, x: Float, y: Float, z: Float): VertexConsumer = addVertex(poseStack.last.pose, x, y, z) fun VertexConsumer.vertex(x: Float, y: Float, z: Float): VertexConsumer = addVertex(x, y, z) fun VertexConsumer.uv(u: Float, v: Float): VertexConsumer = setUv(u, v) fun VertexConsumer.overlayCoords(coords: Int): VertexConsumer = setOverlay(coords) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt index 41f02e250..2d54cc06b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/RenderHelper.kt @@ -4,6 +4,7 @@ import com.mojang.blaze3d.platform.GlStateManager import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.* import net.minecraft.client.renderer.GameRenderer +import net.minecraft.client.renderer.MultiBufferSource import net.minecraft.client.renderer.RenderStateShard import net.minecraft.client.renderer.RenderStateShard.LineStateShard import net.minecraft.client.renderer.RenderType @@ -92,6 +93,76 @@ fun renderRect( } } +private const val IS_WHITE = 1 +private const val IS_TRANSLUCENT = 2 +private const val IS_3D = 4 + +private val rectRenderTypes = Array(1 shl 3) { + val isWhite = it and IS_WHITE != 0 + val isTranslucent = it and IS_TRANSLUCENT != 0 + val is3D = it and IS_3D != 0 + val builder = RenderType.CompositeState.builder() + + if (isWhite) + builder.setShaderState(RenderStateShard.ShaderStateShard(GameRenderer::getPositionShader)) + else + builder.setShaderState(RenderStateShard.ShaderStateShard(GameRenderer::getPositionColorShader)) + + if (!is3D) + builder.setDepthTestState(RenderStateShard.DepthTestStateShard("always", GL_LESS)) + + if (isTranslucent) { + builder.setTransparencyState(RenderStateShard.TransparencyStateShard("normal_blend", { + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + }, { RenderSystem.disableBlend() })) + } + + @Suppress("INACCESSIBLE_TYPE") + RenderType.create( + "otm_rect_${isWhite}_${isTranslucent}_$is3D", + if (isWhite) DefaultVertexFormat.POSITION else DefaultVertexFormat.POSITION_COLOR, + VertexFormat.Mode.QUADS, + 256, + false, + true, + builder.createCompositeState(false) + ) as RenderType +} + +internal fun rectRenderType(isWhite: Boolean, isTranslucent: Boolean, is3D: Boolean): RenderType { + var index = 0 + if (isWhite) index = IS_WHITE + if (isTranslucent) index = index or IS_TRANSLUCENT + if (is3D) index = index or IS_3D + return rectRenderTypes[index] +} + +fun uploadRect( + poseStack: PoseStack, + x: Float, + y: Float, + width: Float, + height: Float, + buffer: MultiBufferSource, + z: Float = 0f, + color: RGBAColor = RGBAColor.WHITE +) { + val builder = buffer.getBuffer(rectRenderType(color.isWhite, color.alpha != 1f, is3DContext)) + + if (color.isWhite) { + builder.vertex(poseStack, x, y + height, z) + builder.vertex(poseStack, x + width, y + height, z) + builder.vertex(poseStack, x + width, y, z) + builder.vertex(poseStack, x, y, z) + } else { + builder.vertex(poseStack, x, y + height, z).color(color) + builder.vertex(poseStack, x + width, y + height, z).color(color) + builder.vertex(poseStack, x + width, y, z).color(color) + builder.vertex(poseStack, x, y, z).color(color) + } +} + @Suppress("NAME_SHADOWING") fun renderCheckerboard( matrix: Matrix4f, 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 f0c59e25c..c31dc9fee 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 @@ -1,10 +1,12 @@ package ru.dbotthepony.mc.otm.client.render.blockentity import com.mojang.blaze3d.vertex.PoseStack +import it.unimi.dsi.fastutil.floats.Float2ObjectArrayMap import net.minecraft.client.renderer.MultiBufferSource import net.minecraft.client.renderer.blockentity.BlockEntityRenderer import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider import net.minecraft.core.Direction +import net.minecraft.network.chat.Component import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.block.tech.EnergyCounterBlock import ru.dbotthepony.mc.otm.block.entity.tech.EnergyCounterBlockEntity @@ -12,6 +14,7 @@ import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.render.* import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.kommons.math.RGBAColor +import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.asAngle import ru.dbotthepony.mc.otm.core.util.formatPower import ru.dbotthepony.mc.otm.core.math.times @@ -47,23 +50,76 @@ class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Con poseStack.rotateAroundPoint(poseStack.translation(), screenDir.asAngle().copy(pitch = PI)) } - var y = -16f + poseStack.scale(1f, 1f, -1f) - val finalX = font.draw(poseStack, buffer = sorse, text = "00000000", gravity = RenderGravity.CENTER_CENTER, x = -4f, y = y, color = RGBAColor.GRAY) - font.draw(poseStack, buffer = sorse, text = "00000000", gravity = RenderGravity.CENTER_CENTER, x = -4f, y = y + font.lineHeight, color = RGBAColor.GRAY) - font.draw(poseStack, buffer = sorse, text = "/t", gravity = RenderGravity.CENTER_LEFT, x = finalX, y = y, color = RGBAColor.GRAY) - font.draw(poseStack, buffer = sorse, text = "/s", gravity = RenderGravity.CENTER_LEFT, x = finalX, y = y + font.lineHeight, color = RGBAColor.GRAY) + if (tile.displayChartOnBlock == 0) { + var y = -16f - 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.history5s.calculateSum(20).toString(0), gravity = RenderGravity.CENTER_RIGHT, x = finalX, y = y + font.lineHeight, color = RGBAColor.WHITE) - poseStack.popPose() + val finalX = font.draw(poseStack, buffer = sorse, text = "00000000", gravity = RenderGravity.CENTER_CENTER, x = -4f, y = y, color = RGBAColor.GRAY) + font.draw(poseStack, buffer = sorse, text = "00000000", gravity = RenderGravity.CENTER_CENTER, x = -4f, y = y + font.lineHeight, color = RGBAColor.GRAY) + font.draw(poseStack, buffer = sorse, text = "/t", gravity = RenderGravity.CENTER_LEFT, x = finalX, y = y, color = RGBAColor.GRAY) + font.draw(poseStack, buffer = sorse, text = "/s", gravity = RenderGravity.CENTER_LEFT, x = finalX, y = y + font.lineHeight, color = RGBAColor.GRAY) - y += font.lineHeight * 3 + poseStack.pushPose() + poseStack.translate(-0.1f, -0.1f, 0.1f) + 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.history5s.calculateSum(20).toString(0), gravity = RenderGravity.CENTER_RIGHT, x = finalX, y = y + font.lineHeight, color = RGBAColor.WHITE) + poseStack.popPose() - font.draw(poseStack, buffer = sorse, text = TOTAL, gravity = RenderGravity.CENTER_CENTER, x = 0f, y = y, color = RGBAColor.WHITE) - font.draw(poseStack, buffer = sorse,text = tile.passed.formatPower(), gravity = RenderGravity.CENTER_CENTER, x = 0f, y = y + font.lineHeight, color = RGBAColor.WHITE) + y += font.lineHeight * 3 + + font.draw(poseStack, buffer = sorse, text = TOTAL, gravity = RenderGravity.CENTER_CENTER, x = 0f, y = y, color = RGBAColor.WHITE) + font.draw(poseStack, buffer = sorse, text = tile.passed.formatPower(), gravity = RenderGravity.CENTER_CENTER, x = 0f, y = y + font.lineHeight, color = RGBAColor.WHITE) + } else { + val chart = tile.charts.getOrNull(tile.displayChartOnBlock - 1) + + if (chart != null) { + val maximum = chart.max() + val normalized: FloatArray + val levelLabels: ChartLevelLabels + + if (maximum.isZero || maximum.isInfinite) { + normalized = FloatArray(chart.width) { + if (chart[it].isInfinite) 0.8f else 0.0f + } + + levelLabels = ChartLevelLabels( + labels = mapOf( + 1f to TextComponent("∞"), + 0f to Decimal.ZERO.formatPower(), + ), + font = font + ) + } else { + normalized = FloatArray(chart.width) { (chart[it] / maximum).toFloat() } + + val map = Float2ObjectArrayMap() + + map[1f] = maximum.formatPower() + map[0.75f] = (maximum * 0.75).formatPower() + map[0.5f] = (maximum * 0.5).formatPower() + map[0.25f] = (maximum * 0.25).formatPower() + + levelLabels = ChartLevelLabels( + labels = map, + font = font + ) + } + + renderChart( + poseStack, + listOf(normalized to RGBAColor.WHITE), + 60f, + 50f, + buffer = sorse, + levelLabels = levelLabels, + x = -30f, + y = -20f, + textScale = 0.3f, + lineWidth = 0.5f + ) + } + } poseStack.popPose() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/sprites/AbstractMatterySprite.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/sprites/AbstractMatterySprite.kt index 6cbab80a2..3837d5ea6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/sprites/AbstractMatterySprite.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/sprites/AbstractMatterySprite.kt @@ -193,9 +193,7 @@ sealed class AbstractMatterySprite : IGUIRenderable, IUVCoords { builder.setTransparencyState(RenderStateShard.TransparencyStateShard("normal_blend", { RenderSystem.enableBlend() RenderSystem.defaultBlendFunc() - }, { - RenderSystem.disableBlend() - })) + }, { RenderSystem.disableBlend() })) @Suppress("INACCESSIBLE_TYPE") RenderType.create("otm_gui_element_no_depth", diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt index dc45fb2fa..3198a2cdd 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/DecimalHistoryChartPanel.kt @@ -35,7 +35,10 @@ open class DecimalHistoryChartPanel>( } levelLabels = ChartLevelLabels( - labels = mapOf(0.8f to formatText(maximum)), + labels = mapOf( + 0.8f to TextComponent("∞"), + // 0f to formatText(Decimal.ZERO), + ), font = font ) } else { 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 9860c62d4..a8ddb1721 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 @@ -10,6 +10,7 @@ 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.CheckBoxLabelInputPanel 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.util.formatPower @@ -19,22 +20,31 @@ class EnergyCounterScreen(menu: EnergyCounterMenu, inventory: Inventory, title: override fun makeMainFrame(): FramePanel> { val frame = super.makeMainFrame()!! - frame.height = 160f - frame.width += 60f + frame.height = 180f + frame.width += 80f - val graphs = linkedMapOf( + val graphs = listOf( menu.history5s to "otm.gui.time.short.5s", menu.history15s to "otm.gui.time.short.15s", menu.history1m to "otm.gui.time.short.1m", + menu.history10m to "otm.gui.time.short.10m", menu.history1h to "otm.gui.time.short.1h", menu.history6h to "otm.gui.time.short.6h", menu.history24h to "otm.gui.time.short.24h", ) - for ((graph, text) in graphs) { - val tab = frame.Tab(activeIcon = TextIcon(color = RGBAColor.BLACK, font = font, text = TranslatableComponent(text))) - val panel = DecimalHistoryChartPanel(this, tab.canvas, graph, formatText = { it.formatPower(formatAsReadable = ShiftPressedCond) }) - panel.dock = Dock.FILL + kotlin.run { + var i = 1 + + for ((graph, text) in graphs) { + val tab = frame.Tab(activeIcon = TextIcon(color = RGBAColor.BLACK, font = font, text = TranslatableComponent(text))) + val panel = DecimalHistoryChartPanel(this, tab.canvas, graph, formatText = { it.formatPower(formatAsReadable = ShiftPressedCond) }) + panel.dock = Dock.FILL + + val current = i++ + val button = CheckBoxLabelInputPanel(this, tab.canvas, menu.displayChartOnBlock.map({ it == current }, { current }), TranslatableComponent("block.overdrive_that_matters.energy_counter.display_this")) + button.dock = Dock.BOTTOM + } } val informationTab = frame.Tab(activeIcon = Widgets18.STATISTICS_TAB) @@ -51,6 +61,7 @@ class EnergyCounterScreen(menu: EnergyCounterMenu, inventory: Inventory, title: 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) })) + labels.add(CheckBoxLabelInputPanel(this, informationTab.canvas, menu.displayChartOnBlock.map({ it == 0 }, { 0 }), TranslatableComponent("block.overdrive_that_matters.energy_counter.display_this"))) for ((i, label) in labels.withIndex()) { if (i == 0) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt index dd158d24a..4add83a9d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt @@ -52,6 +52,14 @@ fun interface BlockPredicate { return BlockPredicate { pos, access -> test(pos, access) || other.test(pos, access) } } + fun and(other: BlockPredicate, vararg others: BlockPredicate): BlockPredicate { + return And(ImmutableSet.copyOf(listOf(other, *others))) + } + + fun or(other: BlockPredicate, vararg others: BlockPredicate): BlockPredicate { + return Or(ImmutableSet.copyOf(listOf(other, *others))) + } + fun offset(offset: BlockPos): Positioned { return Positioned(offset, this) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/AbstractPlayerInputWithFeedback.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/AbstractPlayerInputWithFeedback.kt index c0762f436..4c13c7cb0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/AbstractPlayerInputWithFeedback.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/input/AbstractPlayerInputWithFeedback.kt @@ -15,7 +15,29 @@ import kotlin.reflect.KMutableProperty0 * * Getting and setting values should ONLY be done clientside */ -interface IPlayerInputWithFeedback : ListenableDelegate, Predicate +interface IPlayerInputWithFeedback : ListenableDelegate, Predicate { + private class Map(private val parent: IPlayerInputWithFeedback, private val from: (V) -> N, private val to: (N) -> V): IPlayerInputWithFeedback { + override fun accept(t: N) { + parent.accept(to(t)) + } + + override fun addListener(listener: Consumer): Listenable.L { + return parent.addListener(Consumer { listener.accept(from(it)) }) + } + + override fun get(): N { + return from(parent.get()) + } + + override fun test(t: Player): Boolean { + return parent.test(t) + } + } + + fun map(from: (V) -> N, to: (N) -> V): IPlayerInputWithFeedback { + return Map(this, from, to) + } +} /** * Represents Server to Client synchronization and Client to Server input 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 8fb92837a..a5b9de73c 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 @@ -13,6 +13,7 @@ import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.toDecimal import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback +import ru.dbotthepony.mc.otm.menu.input.IntInputWithFeedback import ru.dbotthepony.mc.otm.registry.MMenus import java.math.BigDecimal @@ -48,11 +49,13 @@ class EnergyCounterMenu( tile.level?.setBlock(tile.blockPos, tile.blockState.setValue(EnergyCounterBlock.INPUT_DIRECTION, tile.blockState.getValue(EnergyCounterBlock.INPUT_DIRECTION).opposite), Block.UPDATE_ALL) } + val displayChartOnBlock = IntInputWithFeedback(this) val redstone = EnumInputWithFeedback(this, RedstoneSetting::class.java) init { if (tile != null) { redstone.with(tile.redstoneControl::redstoneSetting) + displayChartOnBlock.with(tile::displayChartOnBlock) } }