Added energy counter in-world render

This commit is contained in:
DBotThePony 2022-01-25 12:53:22 +07:00
parent 5ff3771f81
commit 6cd04682d7
Signed by: DBot
GPG Key ID: DCC23B5715498507
9 changed files with 635 additions and 11 deletions

View File

@ -261,9 +261,9 @@ object DataGen {
val mdl = if (dir === Direction.WEST) west else east val mdl = if (dir === Direction.WEST) west else east
part().modelFile(mdl).addModel().condition(BlockEnergyCounter.INPUT_DIRECTION, dir).condition(BlockEnergyCounter.IF_DIRECTION, Direction.NORTH) part().modelFile(mdl).addModel().condition(BlockEnergyCounter.INPUT_DIRECTION, dir).condition(BlockEnergyCounter.IF_DIRECTION, Direction.NORTH)
part().modelFile(mdl).rotationX(90).addModel().condition(BlockEnergyCounter.INPUT_DIRECTION, dir).condition(BlockEnergyCounter.IF_DIRECTION, Direction.UP) part().modelFile(mdl).rotationX(-90).addModel().condition(BlockEnergyCounter.INPUT_DIRECTION, dir).condition(BlockEnergyCounter.IF_DIRECTION, Direction.UP)
part().modelFile(mdl).rotationX(180).addModel().condition(BlockEnergyCounter.INPUT_DIRECTION, dir).condition(BlockEnergyCounter.IF_DIRECTION, Direction.SOUTH) part().modelFile(mdl).rotationX(180).addModel().condition(BlockEnergyCounter.INPUT_DIRECTION, dir).condition(BlockEnergyCounter.IF_DIRECTION, Direction.SOUTH)
part().modelFile(mdl).rotationX(-90).addModel().condition(BlockEnergyCounter.INPUT_DIRECTION, dir).condition(BlockEnergyCounter.IF_DIRECTION, Direction.DOWN) part().modelFile(mdl).rotationX(90).addModel().condition(BlockEnergyCounter.INPUT_DIRECTION, dir).condition(BlockEnergyCounter.IF_DIRECTION, Direction.DOWN)
} }
} }
} }

View File

@ -48,6 +48,7 @@ import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityBlackHole;
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityExplosionDebugger; import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityExplosionDebugger;
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntitySphereDebugger; import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntitySphereDebugger;
import ru.dbotthepony.mc.otm.client.render.BlackHoleRenderer; import ru.dbotthepony.mc.otm.client.render.BlackHoleRenderer;
import ru.dbotthepony.mc.otm.client.render.EnergyCounterRenderer;
import ru.dbotthepony.mc.otm.client.render.GravitationStabilizerRenderer; import ru.dbotthepony.mc.otm.client.render.GravitationStabilizerRenderer;
import ru.dbotthepony.mc.otm.client.render.SkinElement; import ru.dbotthepony.mc.otm.client.render.SkinElement;
import ru.dbotthepony.mc.otm.core.Fraction; import ru.dbotthepony.mc.otm.core.Fraction;
@ -1007,6 +1008,7 @@ public class Registry {
public static void registerRenderers(final FMLClientSetupEvent event) { public static void registerRenderers(final FMLClientSetupEvent event) {
BlockEntityRenderers.register(BLACK_HOLE, BlackHoleRenderer::new); BlockEntityRenderers.register(BLACK_HOLE, BlackHoleRenderer::new);
BlockEntityRenderers.register(GRAVITATION_STABILIZER, GravitationStabilizerRenderer::new); BlockEntityRenderers.register(GRAVITATION_STABILIZER, GravitationStabilizerRenderer::new);
BlockEntityRenderers.register(ENERGY_COUNTER, EnergyCounterRenderer::new);
} }
} }

View File

@ -12,10 +12,83 @@ import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
@MethodsReturnNonnullByDefault @MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
public class FormattingHelper { public class FormattingHelper {
public static final TranslatableComponent SUFFIX_KILO = new TranslatableComponent("otm.suffix_raw.kilo");
public static final TranslatableComponent SUFFIX_MEGA = new TranslatableComponent("otm.suffix_raw.mega");
public static final TranslatableComponent SUFFIX_GIGA = new TranslatableComponent("otm.suffix_raw.giga");
public static final TranslatableComponent SUFFIX_TERA = new TranslatableComponent("otm.suffix_raw.tera");
public static final TranslatableComponent SUFFIX_PETA = new TranslatableComponent("otm.suffix_raw.peta");
public static final TranslatableComponent SUFFIX_EXA = new TranslatableComponent("otm.suffix_raw.exa");
public static final TranslatableComponent SUFFIX_ZETTA = new TranslatableComponent("otm.suffix_raw.zetta");
public static final TranslatableComponent SUFFIX_YOTTA = new TranslatableComponent("otm.suffix_raw.yotta");
public static final TranslatableComponent SUFFIX_DECI = new TranslatableComponent("otm.suffix_raw.deci");
public static final TranslatableComponent SUFFIX_CENTI = new TranslatableComponent("otm.suffix_raw.centi");
public static final TranslatableComponent SUFFIX_MILLI = new TranslatableComponent("otm.suffix_raw.milli");
public static final TranslatableComponent SUFFIX_MICRO = new TranslatableComponent("otm.suffix_raw.micro");
public static final TranslatableComponent SUFFIX_NANO = new TranslatableComponent("otm.suffix_raw.nano");
public static final TranslatableComponent SUFFIX_PICO = new TranslatableComponent("otm.suffix_raw.pico");
public static final TranslatableComponent SUFFIX_FEMTO = new TranslatableComponent("otm.suffix_raw.femto");
public static final TranslatableComponent SUFFIX_ATTO = new TranslatableComponent("otm.suffix_raw.atto");
public static final TranslatableComponent SUFFIX_ZEPTO = new TranslatableComponent("otm.suffix_raw.zepto");
public static final TranslatableComponent SUFFIX_YOCTO = new TranslatableComponent("otm.suffix_raw.yocto");
public static final List<TranslatableComponent> BIG_SUFFIX_STRIPE = List.of(
SUFFIX_KILO,
SUFFIX_MEGA,
SUFFIX_GIGA,
SUFFIX_TERA,
SUFFIX_PETA,
SUFFIX_EXA,
SUFFIX_ZETTA,
SUFFIX_YOTTA
);
public static final List<TranslatableComponent> SMALL_SUFFIX_STRIPE = List.of(
SUFFIX_DECI,
SUFFIX_CENTI,
SUFFIX_MILLI,
SUFFIX_MICRO,
SUFFIX_NANO,
SUFFIX_PICO,
SUFFIX_FEMTO,
SUFFIX_ATTO,
SUFFIX_ZEPTO,
SUFFIX_YOCTO
);
public static final List<TranslatableComponent> BIG_SUFFIX_STRIPE_INV;
public static final List<TranslatableComponent> SMALL_SUFFIX_STRIPE_INV;
static {
var list = new ArrayList<TranslatableComponent>(BIG_SUFFIX_STRIPE.size());
int i2 = 0;
for (int i = BIG_SUFFIX_STRIPE.size() - 1; i >= 0; i--) {
list.add(BIG_SUFFIX_STRIPE.get(i2));
i2++;
}
BIG_SUFFIX_STRIPE_INV = List.copyOf(list);
}
static {
var list = new ArrayList<TranslatableComponent>(BIG_SUFFIX_STRIPE.size());
int i2 = 0;
for (int i = BIG_SUFFIX_STRIPE.size() - 1; i >= 0; i--) {
list.add(BIG_SUFFIX_STRIPE.get(i2));
i2++;
}
SMALL_SUFFIX_STRIPE_INV = List.copyOf(list);
}
public static final String[] SUFFIX_COMPONENTS_ABOVE_ONE = new String[] { public static final String[] SUFFIX_COMPONENTS_ABOVE_ONE = new String[] {
"otm.suffix.kilo", "otm.suffix.kilo",
"otm.suffix.mega", "otm.suffix.mega",

View File

@ -7,6 +7,7 @@ import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.simple.SimpleChannel;
import ru.dbotthepony.mc.otm.OverdriveThatMatters; import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.block.entity.EnergyCounterPacket;
import ru.dbotthepony.mc.otm.menu.MenuDriveViewer; import ru.dbotthepony.mc.otm.menu.MenuDriveViewer;
import ru.dbotthepony.mc.otm.menu.data.*; import ru.dbotthepony.mc.otm.menu.data.*;
import ru.dbotthepony.mc.otm.network.android.*; import ru.dbotthepony.mc.otm.network.android.*;
@ -221,5 +222,14 @@ public class MatteryNetworking {
MenuDriveViewer.FilterSetPacket::play, MenuDriveViewer.FilterSetPacket::play,
Optional.of(NetworkDirection.PLAY_TO_SERVER) Optional.of(NetworkDirection.PLAY_TO_SERVER)
); );
CHANNEL.registerMessage(
next_network_id++,
EnergyCounterPacket.class,
EnergyCounterPacket::write,
EnergyCounterPacket.Companion::read,
EnergyCounterPacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
} }
} }

View File

@ -29,9 +29,12 @@ class BlockEnergyCounter : BlockMattery(), EntityBlock {
blockState: BlockState, blockState: BlockState,
blockEntityType: BlockEntityType<T> blockEntityType: BlockEntityType<T>
): BlockEntityTicker<T>? { ): BlockEntityTicker<T>? {
if (level.isClientSide || blockEntityType !== Registry.BlockEntities.ENERGY_COUNTER) if (blockEntityType !== Registry.BlockEntities.ENERGY_COUNTER)
return null return null
if (level.isClientSide)
return BlockEntityTicker { _, _, _, tile -> if (tile is BlockEntityEnergyCounter) tile.clientTick() }
return BlockEntityTicker { _, _, _, tile -> if (tile is BlockEntityEnergyCounter) tile.tick() } return BlockEntityTicker { _, _, _, tile -> if (tile is BlockEntityEnergyCounter) tile.tick() }
} }
@ -49,10 +52,6 @@ class BlockEnergyCounter : BlockMattery(), EntityBlock {
} }
} }
if ((inputDir === Direction.WEST || inputDir === Direction.EAST) && (dir === Direction.UP || dir === Direction.DOWN)) {
dir = dir.opposite
}
return defaultBlockState().setValue(INPUT_DIRECTION, inputDir).setValue(IF_DIRECTION, dir!!) return defaultBlockState().setValue(INPUT_DIRECTION, inputDir).setValue(IF_DIRECTION, dir!!)
} }
@ -96,8 +95,8 @@ class BlockEnergyCounter : BlockMattery(), EntityBlock {
shape = shape.rotateInv(iface) shape = shape.rotateInv(iface)
} else if (input === Direction.EAST || input === Direction.WEST) { } else if (input === Direction.EAST || input === Direction.WEST) {
when (iface) { when (iface) {
Direction.DOWN -> shape = shape.rotateAroundX(-Math.PI / 2) Direction.DOWN -> shape = shape.rotateAroundX(Math.PI / 2)
Direction.UP -> shape = shape.rotateAroundX(Math.PI / 2) Direction.UP -> shape = shape.rotateAroundX(-Math.PI / 2)
Direction.NORTH -> { /* уже в нужном положении */ } Direction.NORTH -> { /* уже в нужном положении */ }
Direction.SOUTH -> shape = shape.rotateAroundX(Math.PI) Direction.SOUTH -> shape = shape.rotateAroundX(Math.PI)
Direction.WEST -> { /* недостижимо */ } Direction.WEST -> { /* недостижимо */ }

View File

@ -1,11 +1,13 @@
package ru.dbotthepony.mc.otm.block.entity package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.client.Minecraft
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.nbt.ByteArrayTag import net.minecraft.nbt.ByteArrayTag
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.IntTag import net.minecraft.nbt.IntTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
@ -18,6 +20,8 @@ import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.CapabilityEnergy import net.minecraftforge.energy.CapabilityEnergy
import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.energy.IEnergyStorage
import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.* import ru.dbotthepony.mc.otm.*
import ru.dbotthepony.mc.otm.block.BlockEnergyCounter import ru.dbotthepony.mc.otm.block.BlockEnergyCounter
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage
@ -26,19 +30,60 @@ import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.receiveEnergy import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.core.Fraction import ru.dbotthepony.mc.otm.core.Fraction
import ru.dbotthepony.mc.otm.menu.MenuEnergyCounter import ru.dbotthepony.mc.otm.menu.MenuEnergyCounter
import ru.dbotthepony.mc.otm.network.MatteryNetworking
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.function.Supplier
data class EnergyCounterPacket(val pos: BlockPos, val thisTick: Fraction, val total: Fraction, val index: Int, val value: Fraction) {
fun write(buff: FriendlyByteBuf) {
buff.writeBlockPos(pos)
thisTick.write(buff)
total.write(buff)
buff.writeInt(index)
value.write(buff)
}
fun play(context: Supplier<NetworkEvent.Context>) {
context.get().packetHandled = true
context.get().enqueueWork {
val ply = Minecraft.getInstance().player ?: return@enqueueWork
val level = ply.level ?: return@enqueueWork
val tile = level.getBlockEntity(pos) as? BlockEntityEnergyCounter ?: return@enqueueWork
tile.lastTick = thisTick
tile.passed = total
tile[index] = value
}
}
companion object {
fun read(buff: FriendlyByteBuf): EnergyCounterPacket {
val pos = buff.readBlockPos()
val thisTick = Fraction.read(buff)
val total = Fraction.read(buff)
val index = buff.readInt()
val value = Fraction.read(buff)
return EnergyCounterPacket(pos, thisTick, total, index, value)
}
}
}
class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : BlockEntityMattery(Registry.BlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) { class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : BlockEntityMattery(Registry.BlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) {
var passed = Fraction.ZERO var passed = Fraction.ZERO
private set internal set
private val history = Array(10 * 20) { Fraction.ZERO } private val history = Array(10 * 20) { Fraction.ZERO }
private var historyTick = 0 private var historyTick = 0
fun size() = history.size fun size() = history.size
operator fun get(i: Int) = history[i] operator fun get(i: Int) = history[i]
internal operator fun set(i: Int, value: Fraction) {
history[i] = value
}
var lastTick: Fraction = Fraction.ZERO var lastTick: Fraction = Fraction.ZERO
private set internal set
fun getHistory(ticks: Int): Array<Fraction> { fun getHistory(ticks: Int): Array<Fraction> {
require(!(ticks < 1 || ticks >= history.size)) { "Invalid history length provided" } require(!(ticks < 1 || ticks >= history.size)) { "Invalid history length provided" }
@ -241,6 +286,33 @@ class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : Blo
return Fraction.ZERO return Fraction.ZERO
} }
override val missingPower: Fraction
get() {
if (is_input) {
if (outputCapability.isPresent) {
val it = outputCapability.resolve().get()
if (it is IMatteryEnergyStorage) {
return it.missingPower
}
return Fraction((it.maxEnergyStored - it.energyStored).coerceAtLeast(0))
}
} else {
if (inputCapability.isPresent) {
val it = inputCapability.resolve().get()
if (it is IMatteryEnergyStorage) {
return it.missingPower
}
return Fraction((it.maxEnergyStored - it.energyStored).coerceAtLeast(0))
}
}
return Fraction.ZERO
}
override fun canExtract() = !is_input override fun canExtract() = !is_input
override fun canReceive() = is_input override fun canReceive() = is_input
} }
@ -339,10 +411,25 @@ class BlockEntityEnergyCounter(p_155229_: BlockPos, p_155230_: BlockState) : Blo
return super.getCapability(cap, side) return super.getCapability(cap, side)
} }
private fun distributor(): PacketDistributor.TargetPoint {
val x = blockPos.x.toDouble()
val y = blockPos.y.toDouble()
val z = blockPos.z.toDouble()
return PacketDistributor.TargetPoint(x, y, z, 32.0, level!!.dimension())
}
fun tick() { fun tick() {
lastTick = history[historyTick] lastTick = history[historyTick]
val index = historyTick
historyTick = (historyTick + 1) % history.size historyTick = (historyTick + 1) % history.size
val accumulated = history[historyTick]
history[historyTick] = Fraction.ZERO history[historyTick] = Fraction.ZERO
MatteryNetworking.CHANNEL.send(PacketDistributor.NEAR.with(this::distributor), EnergyCounterPacket(blockPos, lastTick, passed, index, accumulated))
}
fun clientTick() {
passed += lastTick
} }
companion object { companion object {

View File

@ -0,0 +1,71 @@
package ru.dbotthepony.mc.otm.client.render
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.Minecraft
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.TranslatableComponent
import ru.dbotthepony.mc.otm.block.BlockEnergyCounter
import ru.dbotthepony.mc.otm.block.entity.BlockEntityEnergyCounter
import ru.dbotthepony.mc.otm.core.asAngle
import ru.dbotthepony.mc.otm.core.times
import ru.dbotthepony.mc.otm.menu.FormattingHelper
import kotlin.math.PI
class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<BlockEntityEnergyCounter> {
override fun render(
tile: BlockEntityEnergyCounter,
p_112308_: Float,
poseStack: PoseStack,
p_112310_: MultiBufferSource,
p_112311_: Int,
p_112312_: Int
) {
val screenDir: Direction = tile.blockState.getValue(BlockEnergyCounter.IF_DIRECTION)
val inputDir: Direction = tile.blockState.getValue(BlockEnergyCounter.INPUT_DIRECTION)
poseStack.pushPose()
poseStack.translate(0.5, 0.5, 0.5)
poseStack.translate(screenDir.normal * 0.44)
val font = Minecraft.getInstance().font
poseStack.scale(0.01f, 0.01f, 0.01f)
if (screenDir === Direction.DOWN) {
poseStack.rotateAroundPoint(poseStack.translation(), inputDir.asAngle().copy(pitch = PI, roll = PI / 2))
} else if (screenDir === Direction.UP) {
poseStack.rotateAroundPoint(poseStack.translation(), inputDir.asAngle().copy(pitch = 0.0, roll = PI / 2))
} else {
poseStack.rotateAroundPoint(poseStack.translation(), screenDir.asAngle().copy(pitch = PI))
}
var y = -16f
val finalX = font.drawAligned(poseStack, "00000000", TextAlign.CENTER_CENTER, -4f, y, 0x2C2C2C)
font.drawAligned(poseStack, "00000000", TextAlign.CENTER_CENTER, -4f, y + font.lineHeight, 0x2C2C2C)
font.drawAligned(poseStack, "/t", TextAlign.CENTER_LEFT, finalX.toFloat(), y, 0x2C2C2C)
font.drawAligned(poseStack, "/s", TextAlign.CENTER_LEFT, finalX.toFloat(), y + font.lineHeight, 0x2C2C2C)
poseStack.pushPose()
poseStack.translate(-0.1, -0.1, -0.1)
font.drawAligned(poseStack, tile.lastTick.decimalString(0), TextAlign.CENTER_RIGHT, finalX.toFloat(), y, 0xFFFFFF)
font.drawAligned(poseStack, tile.sumHistory(20).decimalString(0), TextAlign.CENTER_RIGHT, finalX.toFloat(), y + font.lineHeight, 0xFFFFFF)
poseStack.popPose()
y += font.lineHeight * 3
font.drawAligned(poseStack, TOTAL, TextAlign.CENTER_CENTER, 0f, y, 0xFFFFFF)
font.drawAligned(poseStack, FormattingHelper.formatPower(tile.passed), TextAlign.CENTER_CENTER, 0f, y + font.lineHeight, 0xFFFFFF)
poseStack.popPose()
}
override fun getViewDistance() = 32
companion object {
private val TOTAL = TranslatableComponent("otm.gui.total_raw")
}
}

View File

@ -5,6 +5,7 @@ import com.mojang.blaze3d.vertex.VertexConsumer
import com.mojang.math.Matrix4f import com.mojang.math.Matrix4f
import com.mojang.math.Vector3f import com.mojang.math.Vector3f
import net.minecraft.client.gui.Font import net.minecraft.client.gui.Font
import net.minecraft.core.Vec3i
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.util.FormattedCharSequence import net.minecraft.util.FormattedCharSequence
import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.core.*
@ -14,6 +15,7 @@ fun VertexConsumer.vertex(matrix4f: Matrix4f, vector: Vector) = vertex(matrix4f,
fun VertexConsumer.color(color: RGBAColor) = color(color.r, color.g, color.b, color.a) fun VertexConsumer.color(color: RGBAColor) = color(color.r, color.g, color.b, color.a)
fun PoseStack.translate(vector: Vector) = translate(vector.x, vector.y, vector.z) fun PoseStack.translate(vector: Vector) = translate(vector.x, vector.y, vector.z)
fun PoseStack.translate(vector: Vec3i) = translate(vector.x.toDouble(), vector.y.toDouble(), vector.z.toDouble())
fun PoseStack.translate(vector: Vector3f) = last().pose().multiplyWithTranslation(vector.x(), vector.y(), vector.z()) fun PoseStack.translate(vector: Vector3f) = last().pose().multiplyWithTranslation(vector.x(), vector.y(), vector.z())
fun PoseStack.rotateAroundPoint(point: Vector, axis: Vector, rotation: Float, isDegrees: Boolean = false) { fun PoseStack.rotateAroundPoint(point: Vector, axis: Vector, rotation: Float, isDegrees: Boolean = false) {
@ -117,3 +119,362 @@ fun Font.drawAligned(poseStack: PoseStack, text: Component, align: TextAlign, x:
fun Font.drawAligned(poseStack: PoseStack, text: FormattedCharSequence, align: TextAlign, x: Float, y: Float, color: RGBAColor): Int { fun Font.drawAligned(poseStack: PoseStack, text: FormattedCharSequence, align: TextAlign, x: Float, y: Float, color: RGBAColor): Int {
return drawAligned(poseStack, text, align, x, y, color.toInt()) return drawAligned(poseStack, text, align, x, y, color.toInt())
} }
fun Font.drawAlignedLines(poseStack: PoseStack, text: List<Any?>, align: TextAlign, x: Float, y: Float, color: Int) {
var totalWidth = 0
var height = 0
for (line in text) {
var accumulate = 0
when (line) {
is Component -> {
accumulate += width(line)
height += lineHeight
}
is String -> {
accumulate += width(line)
height += lineHeight
}
is FormattedCharSequence -> {
accumulate += width(line)
height += lineHeight
}
else -> height += lineHeight
}
totalWidth = totalWidth.coerceAtLeast(accumulate)
}
if (height == 0 || totalWidth == 0) {
return
}
var y = y
when (align) {
TextAlign.TOP_LEFT -> {
for (line in text) {
when (line) {
is Component -> draw(poseStack, line, x, y, color)
is String -> draw(poseStack, line, x, y, color)
is FormattedCharSequence -> draw(poseStack, line, x, y, color)
}
y += lineHeight
}
}
TextAlign.TOP_CENTER -> {
for (line in text) {
when (line) {
is Component -> draw(poseStack, line, x - width(line) / 2f, y, color)
is String -> draw(poseStack, line, x - width(line) / 2f, y, color)
is FormattedCharSequence -> draw(poseStack, line, x - width(line) / 2f, y, color)
}
y += lineHeight
}
}
TextAlign.TOP_RIGHT -> {
for (line in text) {
when (line) {
is Component -> draw(poseStack, line, x - width(line), y, color)
is String -> draw(poseStack, line, x - width(line), y, color)
is FormattedCharSequence -> draw(poseStack, line, x - width(line), y, color)
}
y += lineHeight
}
}
TextAlign.CENTER_LEFT -> {
for (line in text) {
when (line) {
is Component -> draw(poseStack, line, x, y - height / 2f, color)
is String -> draw(poseStack, line, x, y - height / 2f, color)
is FormattedCharSequence -> draw(poseStack, line, x, y - height / 2f, color)
}
y += lineHeight
}
}
TextAlign.CENTER_CENTER -> {
for (line in text) {
when (line) {
is Component -> draw(poseStack, line, x - width(line) / 2f, y - height / 2f, color)
is String -> draw(poseStack, line, x - width(line) / 2f, y - height / 2f, color)
is FormattedCharSequence -> draw(poseStack, line, x - width(line) / 2f, y - height / 2f, color)
}
y += lineHeight
}
}
TextAlign.CENTER_RIGHT -> {
for (line in text) {
when (line) {
is Component -> draw(poseStack, line, x - width(line), y - height / 2f, color)
is String -> draw(poseStack, line, x - width(line), y - height / 2f, color)
is FormattedCharSequence -> draw(poseStack, line, x - width(line), y - height / 2f, color)
}
y += lineHeight
}
}
TextAlign.BOTTOM_LEFT -> {
for (line in text) {
when (line) {
is Component -> draw(poseStack, line, x, y - height, color)
is String -> draw(poseStack, line, x, y - height, color)
is FormattedCharSequence -> draw(poseStack, line, x, y - height, color)
}
y += lineHeight
}
}
TextAlign.BOTTOM_CENTER -> {
for (line in text) {
when (line) {
is Component -> draw(poseStack, line, x - width(line) / 2f, y - height, color)
is String -> draw(poseStack, line, x - width(line) / 2f, y - height, color)
is FormattedCharSequence -> draw(poseStack, line, x - width(line) / 2f, y - height, color)
}
y += lineHeight
}
}
TextAlign.BOTTOM_RIGHT -> {
for (line in text) {
when (line) {
is Component -> draw(poseStack, line, x - width(line), y - height, color)
is String -> draw(poseStack, line, x - width(line), y - height, color)
is FormattedCharSequence -> draw(poseStack, line, x - width(line), y - height, color)
}
y += lineHeight
}
}
}
}
fun Font.drawAlignedStripe(poseStack: PoseStack, text: List<Any>, align: TextAlign, x: Float, y: Float, color: Int) {
var totalWidth = 0
for (line in text) {
totalWidth += when (line) {
is Component -> {
width(line)
}
is String -> {
width(line)
}
is FormattedCharSequence -> {
width(line)
}
else -> throw IllegalArgumentException("Invalid stripe value ${line}")
}
}
if (totalWidth == 0) {
return
}
var x = x
when (align) {
TextAlign.TOP_LEFT -> {
for (line in text) {
when (line) {
is Component -> {
draw(poseStack, line, x, y, color)
x += width(line)
}
is String -> {
draw(poseStack, line, x, y, color)
x += width(line)
}
is FormattedCharSequence -> {
draw(poseStack, line, x, y, color)
x += width(line)
}
}
}
}
TextAlign.TOP_CENTER -> {
x += totalWidth / 2f
for (line in text) {
when (line) {
is Component -> {
draw(poseStack, line, x, y, color)
x -= width(line)
}
is String -> {
draw(poseStack, line, x, y, color)
x -= width(line)
}
is FormattedCharSequence -> {
draw(poseStack, line, x, y, color)
x -= width(line)
}
}
}
}
TextAlign.TOP_RIGHT -> {
x += totalWidth
for (line in text) {
when (line) {
is Component -> {
draw(poseStack, line, x, y, color)
x -= width(line)
}
is String -> {
draw(poseStack, line, x, y, color)
x -= width(line)
}
is FormattedCharSequence -> {
draw(poseStack, line, x, y, color)
x -= width(line)
}
}
}
}
TextAlign.CENTER_LEFT -> {
for (line in text) {
when (line) {
is Component -> {
draw(poseStack, line, x, y - lineHeight / 2f, color)
x += width(line)
}
is String -> {
draw(poseStack, line, x, y - lineHeight / 2f, color)
x += width(line)
}
is FormattedCharSequence -> {
draw(poseStack, line, x, y - lineHeight / 2f, color)
x += width(line)
}
}
}
}
TextAlign.CENTER_CENTER -> {
x += totalWidth / 2f
for (line in text) {
when (line) {
is Component -> {
draw(poseStack, line, x, y - lineHeight / 2f, color)
x -= width(line)
}
is String -> {
draw(poseStack, line, x, y - lineHeight / 2f, color)
x -= width(line)
}
is FormattedCharSequence -> {
draw(poseStack, line, x, y - lineHeight / 2f, color)
x -= width(line)
}
}
}
}
TextAlign.CENTER_RIGHT -> {
x += totalWidth
for (line in text) {
when (line) {
is Component -> {
draw(poseStack, line, x, y - lineHeight / 2f, color)
x -= width(line)
}
is String -> {
draw(poseStack, line, x, y - lineHeight / 2f, color)
x -= width(line)
}
is FormattedCharSequence -> {
draw(poseStack, line, x, y - lineHeight / 2f, color)
x -= width(line)
}
}
}
}
TextAlign.BOTTOM_LEFT -> {
for (line in text) {
when (line) {
is Component -> {
draw(poseStack, line, x, y - lineHeight, color)
x += width(line)
}
is String -> {
draw(poseStack, line, x, y - lineHeight, color)
x += width(line)
}
is FormattedCharSequence -> {
draw(poseStack, line, x, y - lineHeight, color)
x += width(line)
}
}
}
}
TextAlign.BOTTOM_CENTER -> {
x += totalWidth / 2f
for (line in text) {
when (line) {
is Component -> {
draw(poseStack, line, x, y - lineHeight, color)
x -= width(line)
}
is String -> {
draw(poseStack, line, x, y - lineHeight, color)
x -= width(line)
}
is FormattedCharSequence -> {
draw(poseStack, line, x, y - lineHeight, color)
x -= width(line)
}
}
}
}
TextAlign.BOTTOM_RIGHT -> {
x += totalWidth
for (line in text) {
when (line) {
is Component -> {
draw(poseStack, line, x, y - lineHeight, color)
x -= width(line)
}
is String -> {
draw(poseStack, line, x, y - lineHeight, color)
x -= width(line)
}
is FormattedCharSequence -> {
draw(poseStack, line, x, y - lineHeight, color)
x -= width(line)
}
}
}
}
}
}

View File

@ -24,6 +24,8 @@
"otm.gui.progress_widget": "Progress: %s%%", "otm.gui.progress_widget": "Progress: %s%%",
"otm.gui.progress_widget_stuck": "The machine can not work, check configuration", "otm.gui.progress_widget_stuck": "The machine can not work, check configuration",
"otm.gui.total_raw": "Total:",
"otm.gui.matter.percentage_level": "Matter level: %s%%", "otm.gui.matter.percentage_level": "Matter level: %s%%",
"otm.gui.matter.format": "Matter: %s", "otm.gui.matter.format": "Matter: %s",
"otm.gui.matter.name": "MtU", "otm.gui.matter.name": "MtU",
@ -142,6 +144,25 @@
"otm.suffix.zepto": "%s z%s", "otm.suffix.zepto": "%s z%s",
"otm.suffix.yocto": "%s y%s", "otm.suffix.yocto": "%s y%s",
"otm.suffix_raw.kilo": "k",
"otm.suffix_raw.mega": "M",
"otm.suffix_raw.giga": "G",
"otm.suffix_raw.tera": "T",
"otm.suffix_raw.peta": "P",
"otm.suffix_raw.exa": "E",
"otm.suffix_raw.zetta": "Z",
"otm.suffix_raw.yotta": "Y",
"otm.suffix_raw.deci": "d",
"otm.suffix_raw.centi": "c",
"otm.suffix_raw.milli": "m",
"otm.suffix_raw.micro": "μ",
"otm.suffix_raw.nano": "n",
"otm.suffix_raw.pico": "p",
"otm.suffix_raw.femto": "f",
"otm.suffix_raw.atto": "a",
"otm.suffix_raw.zepto": "z",
"otm.suffix_raw.yocto": "y",
"death.attack.otm_become_android": "%1$s lost their humanity", "death.attack.otm_become_android": "%1$s lost their humanity",
"death.attack.otm_become_humane": "%1$s regained their humanity", "death.attack.otm_become_humane": "%1$s regained their humanity",
"death.attack.otm_event_horizon": "%1$s never crossed event horizon", "death.attack.otm_event_horizon": "%1$s never crossed event horizon",