diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/OverdriveThatMatters.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/OverdriveThatMatters.kt index 4a793d469..c3756dcfd 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/OverdriveThatMatters.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/OverdriveThatMatters.kt @@ -129,6 +129,8 @@ object OverdriveThatMatters { MOD_BUS.addListener(EventPriority.NORMAL, ChartTooltipElement.Companion::register) + MOD_BUS.addListener(EventPriority.HIGH, MatteryGUI::registerGuiLayers) + MBlockColors.register(MOD_BUS) } @@ -206,7 +208,6 @@ object OverdriveThatMatters { FORGE_BUS.addListener(EventPriority.NORMAL, MatteryGUI::onScreenRender) FORGE_BUS.addListener(EventPriority.LOWEST, MatteryGUI::onOpenGUIEvent) - FORGE_BUS.addListener(EventPriority.NORMAL, MatteryGUI::onRenderGuiEvent) FORGE_BUS.addListener(EventPriority.HIGH, MatteryGUI::onLayerRenderEvent) FORGE_BUS.addListener(EventPriority.NORMAL, ShockwaveRenderer::onRender) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/MatteryGUI.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/MatteryGUI.kt index fa7a9462b..8fc71240d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/MatteryGUI.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/MatteryGUI.kt @@ -3,9 +3,10 @@ package ru.dbotthepony.mc.otm.client import com.google.common.collect.ImmutableList import com.mojang.blaze3d.systems.RenderSystem import net.minecraft.ChatFormatting +import net.minecraft.client.DeltaTracker import net.minecraft.client.gui.Font -import net.minecraft.client.gui.Gui import net.minecraft.client.gui.GuiGraphics +import net.minecraft.client.gui.LayeredDraw import net.minecraft.client.gui.components.Button import net.minecraft.client.gui.screens.DeathScreen import net.minecraft.client.gui.screens.InBedChatScreen @@ -16,12 +17,11 @@ import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.player.Player import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ShieldItem -import net.neoforged.neoforge.client.event.RenderGuiEvent +import net.neoforged.neoforge.client.event.RegisterGuiLayersEvent import net.neoforged.neoforge.client.event.RenderGuiLayerEvent import net.neoforged.neoforge.client.event.ScreenEvent import net.neoforged.neoforge.client.gui.VanillaGuiLayers import net.neoforged.neoforge.common.ItemAbilities -import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.android.feature.NanobotsArmorFeature import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.capability.MatteryPlayer @@ -32,7 +32,7 @@ import ru.dbotthepony.mc.otm.client.render.sprites.MatterySprite import ru.dbotthepony.mc.otm.config.ClientConfig import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.kommons.math.RGBAColor -import ru.dbotthepony.mc.otm.core.ResourceLocation +import ru.dbotthepony.mc.otm.OverdriveThatMatters.loc import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.random import ru.dbotthepony.mc.otm.core.util.formatPower @@ -42,24 +42,6 @@ import kotlin.math.PI import kotlin.math.ceil object MatteryGUI { - private val BARS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/player_bars.png"), 81f, 36f) - val CHARGE = BARS.sprite(height = 9f) - val CHARGE_BG = BARS.sprite(y = 9f, height = 9f) - - val CHARGE_HUNGER = BARS.sprite(y = 18f, height = 9f) - val CHARGE_HUNGER_BG = BARS.sprite(y = 27f, height = 9f) - - private val BARS_HP = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/player_bars_health.png"), 81f, 63f) - - val HEALTH = BARS_HP.sprite(height = 9f) - val HEALTH_BG = BARS_HP.sprite(y = 9f, height = 9f) - val HEALTH_BG_NANOBOTS = BARS_HP.sprite(y = 18f, height = 9f) - - val HEALTH_POISON = BARS_HP.sprite(y = 27f, height = 9f) - val HEALTH_WITHER = BARS_HP.sprite(y = 36f, height = 9f) - val HEALTH_ABSORB = BARS_HP.sprite(y = 45f, height = 9f) - val HEALTH_FROZEN = BARS_HP.sprite(y = 54f, height = 9f) - private var originalBedButtonX = -1 private var originalBedButtonY = -1 @@ -112,349 +94,397 @@ object MatteryGUI { } } - var iteration = 0 - var showIterationUntil = 0L - var showIterationUntilFade = 0L - - val deathLog = ArrayList>() - - private fun showIteration(event: RenderGuiEvent.Post) { - val time = System.currentTimeMillis() - - if (time > showIterationUntilFade) { - return - } - - val guiGraphics = MGUIGraphics(event.guiGraphics) - val stack = guiGraphics.pose - val window = minecraft.window - - stack.pushPose() - - val negateScale = 1f / window.guiScale.toFloat() - var modifyScale = 6f * negateScale - val finalScale = modifyScale * window.guiScale.toFloat() - - stack.scale(modifyScale, modifyScale, modifyScale) - - var width = window.guiScaledWidth / modifyScale - var height = window.guiScaledHeight / modifyScale - - var x = width / 2f - var y = height / 2f - - val progress = if (time < showIterationUntil) 1f else 1f - (time - showIterationUntil).toFloat() / (showIterationUntilFade - showIterationUntil).toFloat() - val scissorBase = (y - 12f) * finalScale - val scissorHeight = (12f + (deathLog.size - 2f).coerceAtLeast(0f) * minecraft.font.lineHeight * modifyScale * 0.0875f) * finalScale - - pushScissorRect(0, (scissorBase + scissorHeight * (1f - progress)).toInt(), window.width, (scissorHeight * progress * 2f).toInt()) - - guiGraphics.renderRect( - 0f, - y - 12f, - window.guiScaledWidth.toFloat(), - 24f + (deathLog.size - 2f).coerceAtLeast(0f) * minecraft.font.lineHeight * modifyScale * 0.175f, - color = RGBAColor(1f, 1f, 1f, 0.4f)) - - val text = TranslatableComponent("otm.iteration", iteration) - - guiGraphics.draw(text, x + 1f, y + 1f, color = RGBAColor.BLACK, gravity = RenderGravity.CENTER_CENTER) - guiGraphics.draw(text, x, y, color = RGBAColor.WHITE, gravity = RenderGravity.CENTER_CENTER) - - stack.scale(0.35f, 0.35f, 0.35f) - - modifyScale *= 0.35f - - width = window.guiScaledWidth / modifyScale - height = window.guiScaledHeight / modifyScale - - x = width / 2f - y = height / 2f + minecraft.font.lineHeight * finalScale * 0.35f - - var color = 0xFF - - for (i in deathLog.indices.reversed()) { - val component = deathLog[i] - guiGraphics.draw(component.second, x + 1f, y + 1f, color = RGBAColor.BLACK, gravity = RenderGravity.CENTER_CENTER) - guiGraphics.draw(component.second, x, y, color = RGBAColor(color, color, color), gravity = RenderGravity.CENTER_CENTER) - - y += minecraft.font.lineHeight - color = (color - 0x20).coerceAtLeast(0x0) - } - - stack.popPose() - - popScissorRect() - } - - private val powerAlert = TranslatableComponent( - "otm.pwr_alert1", - TranslatableComponent("otm.pwr_alert2").withStyle(ChatFormatting.RED) - ).withStyle(ChatFormatting.YELLOW) - - private val powerAlert2 = TranslatableComponent("otm.pwr_alert3").withStyle(ChatFormatting.YELLOW) - private val powerAlert3 = TranslatableComponent("otm.pwr_alert4").withStyle(ChatFormatting.YELLOW) - - private val errorCodes = ImmutableList.of( - "005", "06E", "004", "0F1", "7FF", "F0A" - ) - - private var errorCode = errorCodes[1] - private var errorAddress = "0".repeat(16) - - private val registerParts = ImmutableList.of("A", "B", "C", "D", "E", "F", "M", "D", "X", "Y", "Z") - private var errorRegister = "VXE VMMXDE" - - private val errorServerNode by lazy { - (minecraft.player!!.uuid.leastSignificantBits and 0xFFFFFF).toString(16).uppercase() - } - - private var displayPowerError = false - private var lastDisplayPowerErrorUpdate = System.nanoTime() - private var targetErrorAlpha = System.nanoTime() - - private fun drawLowPower(event: RenderGuiEvent.Post, player: LocalPlayer) { - // yes, this is a reference to movie Transcendence (2014) - if (player.matteryPlayer.androidEnergy.batteryLevel > Decimal.TEN) - return - - if (System.nanoTime() - lastDisplayPowerErrorUpdate > 100_000_000L) { - lastDisplayPowerErrorUpdate = System.nanoTime() - val newStatus = player.random.nextBoolean() - - if (newStatus != displayPowerError && newStatus) { - targetErrorAlpha = System.nanoTime() - errorCode = errorCodes[player.random.nextInt(errorCodes.size)] - errorRegister = "V${registerParts.random(player.random)}${registerParts.random(player.random)} V${registerParts.random(player.random)}${registerParts.random(player.random)}${registerParts.random(player.random)}${registerParts.random(player.random)}${registerParts.random(player.random)}" - errorAddress = java.lang.Long.toUnsignedString(player.random.nextLong(), 16).uppercase() - - if (errorAddress.length < 16) { - errorAddress = "0".repeat(16 - errorAddress.length) + errorAddress - } + fun onLayerRenderEvent(event: RenderGuiLayerEvent.Pre) { + if (minecraft.player?.matteryPlayer?.isAndroid == true) { + if (event.name == VanillaGuiLayers.FOOD_LEVEL + || event.name == VanillaGuiLayers.AIR_LEVEL + || (event.name == VanillaGuiLayers.PLAYER_HEALTH && ClientConfig.HUD.ANDROID_HEALTH_BAR)) { + event.isCanceled = true } - - displayPowerError = newStatus } - - if (!displayPowerError) return - - val alpha: Float - val diff = System.nanoTime() - targetErrorAlpha - - if (diff > 50_000_000L) { - alpha = 1f - } else { - alpha = diff / 50_000_000f - } - - if (alpha != 1f) RenderSystem.setShaderColor(1f, 1f, 1f, alpha) - val window = minecraft.window - - val x = window.guiScaledWidth / 2f - var y = window.guiScaledHeight / 2.5f - - minecraft.font.draw( - event.guiGraphics.pose(), - powerAlert, - x, y, - scale = 4f, - gravity = RenderGravity.CENTER_CENTER, - drawShadow = true - ) - - y += 20f + minecraft.font.lineHeight * 4f - - minecraft.font.draw( - event.guiGraphics.pose(), - powerAlert2, - x, y, - gravity = RenderGravity.CENTER_CENTER, - drawShadow = true - ) - - y += minecraft.font.lineHeight + 2f - - minecraft.font.draw( - event.guiGraphics.pose(), - powerAlert3, - x, y, - gravity = RenderGravity.CENTER_CENTER, - drawShadow = true - ) - - y += minecraft.font.lineHeight * 2f + 4f - - minecraft.font.draw( - event.guiGraphics.pose(), - TranslatableComponent( - "otm.pwr_alert5", - errorCode, - errorServerNode, - errorAddress, - errorRegister - ).withStyle(ChatFormatting.YELLOW), - x, y, - gravity = RenderGravity.CENTER_CENTER, - drawShadow = true, - ) - - if (alpha != 1f) RenderSystem.setShaderColor(1f, 1f, 1f, 1f) } - fun onRenderGuiEvent(event: RenderGuiEvent.Post) { - val player = minecraft.player ?: return + fun registerGuiLayers(event: RegisterGuiLayersEvent) { + event.registerBelow(VanillaGuiLayers.FOOD_LEVEL, loc("android_energy_bar"), AndroidEnergyBarLayer()) + event.registerBelow(VanillaGuiLayers.PLAYER_HEALTH, loc("android_health_bar"), AndroidHealthBarLayer()) - if (!player.matteryPlayer.isAndroid) { - return - } - - showIteration(event) - drawLowPower(event, player) + event.registerAboveAll(loc("android_iteration"), AndroidIterationLayer()) + event.registerAbove(VanillaGuiLayers.CAMERA_OVERLAYS, loc("android_low_power"), AndroidLowPowerLayer()) } - private fun renderFoodAndAir(event: RenderGuiLayerEvent.Pre, gui: Gui) { - val ply: LocalPlayer = minecraft.player ?: return + class AndroidEnergyBarLayer : LayeredDraw.Layer { + override fun render( + graphics: GuiGraphics, + delta: DeltaTracker + ) { + val ply: LocalPlayer = minecraft.player ?: return - if (ply.vehicle is LivingEntity) - return - - val mattery = ply.matteryPlayer - - if (mattery.isAndroid) { - event.isCanceled = true - - if (event.name === VanillaGuiLayers.AIR_LEVEL) + if (ply.vehicle is LivingEntity || ply.isCreative) return - var level: Float + val gui = minecraft.gui + val mattery = ply.matteryPlayer - if (mattery.androidEnergy.maxBatteryLevel.isZero) { - level = 0f + if (mattery.isAndroid) { + var level: Float + + if (mattery.androidEnergy.maxBatteryLevel.isZero) { + level = 0f + } else { + level = mattery.androidEnergy.batteryLevel.percentage(mattery.androidEnergy.maxBatteryLevel) + + if (level >= 0.98f) + level = 1f + } + + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + RenderSystem.disableDepthTest() + RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) + + val width = minecraft.window.guiScaledWidth + val height = minecraft.window.guiScaledHeight + val left = width / 2 + 10 + val top: Int = height - gui.rightHeight + gui.rightHeight += 10 + + val leftPadding = ceil(level * 80f - 0.5f) + + val guiGraphics = MGUIGraphics(graphics) + + if (ply.hasEffect(MobEffects.HUNGER)) { + CHARGE_HUNGER_BG.render(guiGraphics, left.toFloat(), top.toFloat()) + CHARGE_HUNGER.renderPartial(guiGraphics, left.toFloat() - leftPadding + 80f, top.toFloat(), width = leftPadding) + } else { + CHARGE_BG.render(guiGraphics, left.toFloat(), top.toFloat()) + CHARGE.renderPartial(guiGraphics, left.toFloat() - leftPadding + 80f, top.toFloat(), width = leftPadding) + } + + val formattedPower = mattery.androidEnergy.batteryLevel.formatPower() + + val scale = ClientConfig.HUD.BAR_TEXT_SCALE.toFloat() + guiGraphics.draw(formattedPower, left + CHARGE_BG.width + 2f + scale, top + CHARGE_BG.height / 2f + scale, font = gui.font, scale = scale, gravity = RenderGravity.CENTER_LEFT, color = RGBAColor.YELLOW, drawOutline = true) + + RenderSystem.disableBlend() + RenderSystem.enableDepthTest() + } + } + + companion object { + private val BARS = MatteryAtlas(loc("textures/gui/player_bars.png"), 81f, 36f) + val CHARGE = BARS.sprite(height = 9f) + val CHARGE_BG = BARS.sprite(y = 9f, height = 9f) + + val CHARGE_HUNGER = BARS.sprite(y = 18f, height = 9f) + val CHARGE_HUNGER_BG = BARS.sprite(y = 27f, height = 9f) + } + } + + class AndroidHealthBarLayer : LayeredDraw.Layer { + override fun render( + graphics: GuiGraphics, + delta: DeltaTracker + ) { + if (!ClientConfig.HUD.ANDROID_HEALTH_BAR) return + + val gui = minecraft.gui + val ply: LocalPlayer = minecraft.player ?: return + val mattery = ply.matteryPlayer + + if (mattery.isAndroid && !ply.isCreative) { + val level: Float = (ply.health / ply.maxHealth).coerceIn(0.0f, 1.0f) + val levelAbsorb: Float = (ply.absorptionAmount / ply.maxHealth).coerceIn(0.0f, 1.0f) + + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + RenderSystem.disableDepthTest() + RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) + + val width = minecraft.window.guiScaledWidth + val height = minecraft.window.guiScaledHeight + val left = width / 2 - 10 - 81 + val top: Int = height - gui.leftHeight + gui.leftHeight += 10 + + val guiGraphics = MGUIGraphics(graphics) + + HEALTH_BG.render(guiGraphics, left.toFloat(), top.toFloat()) + + if (mattery.hasFeature(AndroidFeatures.NANOBOTS_ARMOR)) { + val featArmor = mattery.getFeature(AndroidFeatures.NANOBOTS_ARMOR) as NanobotsArmorFeature + val levelArmor: Float = (featArmor.layers.toFloat() / (featArmor.strength + 1).toFloat()).coerceIn(0.0f, 1.0f) + + HEALTH_BG_NANOBOTS.renderPartial(guiGraphics, left.toFloat(), top.toFloat(), width = ceil(levelArmor * 81f)) + } + + getSpriteForPlayer(ply).renderPartial(guiGraphics, left.toFloat(), top.toFloat(), width = ceil(level * 80f - 0.5f)) + if (levelAbsorb > 0) { + HEALTH_ABSORB.renderPartial(guiGraphics, left.toFloat(), top.toFloat(), width = ceil(levelAbsorb * 80f - 0.5f)) + } + + var formattedHealth = TextComponent("%d/%d".format(ply.health.toInt(), ply.maxHealth.toInt())) + + if (ply.absorptionAmount > 0) + formattedHealth = TextComponent("%d+%d/%d".format(ply.health.toInt(), ply.absorptionAmount.toInt(), ply.maxHealth.toInt())) + + val scale = ClientConfig.HUD.BAR_TEXT_SCALE.toFloat() + guiGraphics.draw(formattedHealth, left - 2f, top + HEALTH_BG.height / 2f + 1f * scale, scale = scale, gravity = RenderGravity.CENTER_RIGHT, color = getHealthColorForPlayer(ply), drawOutline = true) + + RenderSystem.disableBlend() + RenderSystem.enableDepthTest() + } + } + + companion object { + private val BARS_HP = MatteryAtlas(loc("textures/gui/player_bars_health.png"), 81f, 63f) + + val HEALTH = BARS_HP.sprite(height = 9f) + val HEALTH_BG = BARS_HP.sprite(y = 9f, height = 9f) + val HEALTH_BG_NANOBOTS = BARS_HP.sprite(y = 18f, height = 9f) + + val HEALTH_POISON = BARS_HP.sprite(y = 27f, height = 9f) + val HEALTH_WITHER = BARS_HP.sprite(y = 36f, height = 9f) + val HEALTH_ABSORB = BARS_HP.sprite(y = 45f, height = 9f) + val HEALTH_FROZEN = BARS_HP.sprite(y = 54f, height = 9f) + + private fun getSpriteForPlayer(player: Player): MatterySprite { + if (player.hasEffect(MobEffects.POISON)) { + return HEALTH_POISON + } else if (player.hasEffect(MobEffects.WITHER)) { + return HEALTH_WITHER + } else if (player.isFullyFrozen) { + return HEALTH_FROZEN + } + + return HEALTH + } + + private fun getHealthColorForPlayer(player: Player): RGBAColor { + if (player.hasEffect(MobEffects.POISON)) { + return RGBAColor.DARK_GREEN + } else if (player.hasEffect(MobEffects.WITHER)) { + return RGBAColor.WHITE + } else if (player.isFullyFrozen) { + return RGBAColor.AQUA + } + + return RGBAColor.RED + } // можно вынести в конфиг, но для этого нужен селектор цвета + + } + } + + class AndroidIterationLayer : LayeredDraw.Layer { + override fun render( + graphics: GuiGraphics, + delta: DeltaTracker + ) { + val player = minecraft.player ?: return + + if (!player.matteryPlayer.isAndroid) return + + val time = System.currentTimeMillis() + + if (time > showIterationUntilFade) { + return + } + + val guiGraphics = MGUIGraphics(graphics) + val stack = guiGraphics.pose + val window = minecraft.window + + stack.pushPose() + + val negateScale = 1f / window.guiScale.toFloat() + var modifyScale = 6f * negateScale + val finalScale = modifyScale * window.guiScale.toFloat() + + stack.scale(modifyScale, modifyScale, modifyScale) + + var width = window.guiScaledWidth / modifyScale + var height = window.guiScaledHeight / modifyScale + + var x = width / 2f + var y = height / 2f + + val progress = if (time < showIterationUntil) 1f else 1f - (time - showIterationUntil).toFloat() / (showIterationUntilFade - showIterationUntil).toFloat() + val scissorBase = (y - 12f) * finalScale + val scissorHeight = (12f + (deathLog.size - 2f).coerceAtLeast(0f) * minecraft.font.lineHeight * modifyScale * 0.0875f) * finalScale + + pushScissorRect(0, (scissorBase + scissorHeight * (1f - progress)).toInt(), window.width, (scissorHeight * progress * 2f).toInt()) + + guiGraphics.renderRect( + 0f, + y - 12f, + window.guiScaledWidth.toFloat(), + 24f + (deathLog.size - 2f).coerceAtLeast(0f) * minecraft.font.lineHeight * modifyScale * 0.175f, + color = RGBAColor(1f, 1f, 1f, 0.4f)) + + val text = TranslatableComponent("otm.iteration", iteration) + + guiGraphics.draw(text, x + 1f, y + 1f, color = RGBAColor.BLACK, gravity = RenderGravity.CENTER_CENTER) + guiGraphics.draw(text, x, y, color = RGBAColor.WHITE, gravity = RenderGravity.CENTER_CENTER) + + stack.scale(0.35f, 0.35f, 0.35f) + + modifyScale *= 0.35f + + width = window.guiScaledWidth / modifyScale + height = window.guiScaledHeight / modifyScale + + x = width / 2f + y = height / 2f + minecraft.font.lineHeight * finalScale * 0.35f + + var color = 0xFF + + for (i in deathLog.indices.reversed()) { + val component = deathLog[i] + guiGraphics.draw(component.second, x + 1f, y + 1f, color = RGBAColor.BLACK, gravity = RenderGravity.CENTER_CENTER) + guiGraphics.draw(component.second, x, y, color = RGBAColor(color, color, color), gravity = RenderGravity.CENTER_CENTER) + + y += minecraft.font.lineHeight + color = (color - 0x20).coerceAtLeast(0x0) + } + + stack.popPose() + + popScissorRect() + } + + companion object { + var iteration = 0 + var showIterationUntil = 0L + var showIterationUntilFade = 0L + + val deathLog = ArrayList>() + } + } + + class AndroidLowPowerLayer : LayeredDraw.Layer { + private var errorCode = errorCodes[1] + private var errorAddress = "0".repeat(16) + + private var errorRegister = "VXE VMMXDE" + + private val errorServerNode by lazy { + (minecraft.player!!.uuid.leastSignificantBits and 0xFFFFFF).toString(16).uppercase() + } + + private var displayPowerError = false + private var lastDisplayPowerErrorUpdate = System.nanoTime() + private var targetErrorAlpha = System.nanoTime() + + override fun render( + graphics: GuiGraphics, + delta: DeltaTracker + ) { + val player = minecraft.player ?: return + + if (!player.matteryPlayer.isAndroid) { + return + } + + // yes, this is a reference to movie Transcendence (2014) + if (player.matteryPlayer.androidEnergy.batteryLevel > Decimal.TEN) + return + + if (System.nanoTime() - lastDisplayPowerErrorUpdate > 100_000_000L) { + lastDisplayPowerErrorUpdate = System.nanoTime() + val newStatus = player.random.nextBoolean() + + if (newStatus != displayPowerError && newStatus) { + targetErrorAlpha = System.nanoTime() + errorCode = errorCodes[player.random.nextInt(errorCodes.size)] + errorRegister = "V${registerParts.random(player.random)}${registerParts.random(player.random)} V${registerParts.random(player.random)}${registerParts.random(player.random)}${registerParts.random(player.random)}${registerParts.random(player.random)}${registerParts.random(player.random)}" + errorAddress = java.lang.Long.toUnsignedString(player.random.nextLong(), 16).uppercase() + + if (errorAddress.length < 16) { + errorAddress = "0".repeat(16 - errorAddress.length) + errorAddress + } + } + + displayPowerError = newStatus + } + + if (!displayPowerError) return + + val alpha: Float + val diff = System.nanoTime() - targetErrorAlpha + + if (diff > 50_000_000L) { + alpha = 1f } else { - level = mattery.androidEnergy.batteryLevel.percentage(mattery.androidEnergy.maxBatteryLevel) - - if (level >= 0.98f) - level = 1f + alpha = diff / 50_000_000f } - RenderSystem.enableBlend() - RenderSystem.defaultBlendFunc() - RenderSystem.disableDepthTest() - RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) + if (alpha != 1f) RenderSystem.setShaderColor(1f, 1f, 1f, alpha) + val window = minecraft.window - val width = minecraft.window.guiScaledWidth - val height = minecraft.window.guiScaledHeight - val left = width / 2 + 10 - val top: Int = height - gui.rightHeight - gui.rightHeight += 10 + val x = window.guiScaledWidth / 2f + var y = window.guiScaledHeight / 2.5f - val leftPadding = ceil(level * 80f - 0.5f) + minecraft.font.draw( + graphics.pose(), + powerAlert, + x, y, + scale = 4f, + gravity = RenderGravity.CENTER_CENTER, + drawShadow = true + ) - val guiGraphics = MGUIGraphics(event.guiGraphics) + y += 20f + minecraft.font.lineHeight * 4f - if (ply.hasEffect(MobEffects.HUNGER)) { - CHARGE_HUNGER_BG.render(guiGraphics, left.toFloat(), top.toFloat()) - CHARGE_HUNGER.renderPartial(guiGraphics, left.toFloat() - leftPadding + 80f, top.toFloat(), width = leftPadding) - } else { - CHARGE_BG.render(guiGraphics, left.toFloat(), top.toFloat()) - CHARGE.renderPartial(guiGraphics, left.toFloat() - leftPadding + 80f, top.toFloat(), width = leftPadding) - } + minecraft.font.draw( + graphics.pose(), + powerAlert2, + x, y, + gravity = RenderGravity.CENTER_CENTER, + drawShadow = true + ) - val formattedPower = mattery.androidEnergy.batteryLevel.formatPower() + y += minecraft.font.lineHeight + 2f - val scale = ClientConfig.HUD.BAR_TEXT_SCALE.toFloat() - guiGraphics.draw(formattedPower, left + CHARGE_BG.width + 2f + scale, top + CHARGE_BG.height / 2f + scale, font = gui.font, scale = scale, gravity = RenderGravity.CENTER_LEFT, color = RGBAColor.YELLOW, drawOutline = true) + minecraft.font.draw( + graphics.pose(), + powerAlert3, + x, y, + gravity = RenderGravity.CENTER_CENTER, + drawShadow = true + ) - RenderSystem.disableBlend() - RenderSystem.enableDepthTest() - } - } + y += minecraft.font.lineHeight * 2f + 4f - private fun getSpriteForPlayer(player: Player): MatterySprite { - if (player.hasEffect(MobEffects.POISON)) { - return HEALTH_POISON - } else if (player.hasEffect(MobEffects.WITHER)) { - return HEALTH_WITHER - } else if (player.isFullyFrozen) { - return HEALTH_FROZEN + minecraft.font.draw( + graphics.pose(), + TranslatableComponent( + "otm.pwr_alert5", + errorCode, + errorServerNode, + errorAddress, + errorRegister + ).withStyle(ChatFormatting.YELLOW), + x, y, + gravity = RenderGravity.CENTER_CENTER, + drawShadow = true, + ) + + if (alpha != 1f) RenderSystem.setShaderColor(1f, 1f, 1f, 1f) } - return HEALTH - } + companion object { + private val powerAlert = TranslatableComponent( + "otm.pwr_alert1", + TranslatableComponent("otm.pwr_alert2").withStyle(ChatFormatting.RED) + ).withStyle(ChatFormatting.YELLOW) - private fun getHealthColorForPlayer(player: Player): RGBAColor { - if (player.hasEffect(MobEffects.POISON)) { - return RGBAColor.DARK_GREEN - } else if (player.hasEffect(MobEffects.WITHER)) { - return RGBAColor.WHITE - } else if (player.isFullyFrozen) { - return RGBAColor.AQUA - } + private val powerAlert2 = TranslatableComponent("otm.pwr_alert3").withStyle(ChatFormatting.YELLOW) + private val powerAlert3 = TranslatableComponent("otm.pwr_alert4").withStyle(ChatFormatting.YELLOW) - return RGBAColor.RED - } // можно вынести в конфиг, но для этого нужен селектор цвета + private val errorCodes = ImmutableList.of( + "005", "06E", "004", "0F1", "7FF", "F0A" + ) - private fun renderPlayerHealth(event: RenderGuiLayerEvent.Pre, gui: Gui) { - if (!ClientConfig.HUD.ANDROID_HEALTH_BAR) return - - val ply: LocalPlayer = minecraft.player ?: return - val mattery = ply.matteryPlayer - - if (mattery.isAndroid) { - event.isCanceled = true - - val level: Float = (ply.health / ply.maxHealth).coerceIn(0.0f, 1.0f) - val levelAbsorb: Float = (ply.absorptionAmount / ply.maxHealth).coerceIn(0.0f, 1.0f) - - RenderSystem.enableBlend() - RenderSystem.defaultBlendFunc() - RenderSystem.disableDepthTest() - RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) - - val width = minecraft.window.guiScaledWidth - val height = minecraft.window.guiScaledHeight - val left = width / 2 - 10 - 81 - val top: Int = height - gui.leftHeight - gui.leftHeight += 10 - - val guiGraphics = MGUIGraphics(event.guiGraphics) - - HEALTH_BG.render(guiGraphics, left.toFloat(), top.toFloat()) - - if (mattery.hasFeature(AndroidFeatures.NANOBOTS_ARMOR)) { - val featArmor = mattery.getFeature(AndroidFeatures.NANOBOTS_ARMOR) as NanobotsArmorFeature - val levelArmor: Float = (featArmor.layers.toFloat() / (featArmor.strength + 1).toFloat()).coerceIn(0.0f, 1.0f) - - HEALTH_BG_NANOBOTS.renderPartial(guiGraphics, left.toFloat(), top.toFloat(), width = ceil(levelArmor * 81f)) - } - - getSpriteForPlayer(ply).renderPartial(guiGraphics, left.toFloat(), top.toFloat(), width = ceil(level * 80f - 0.5f)) - if (levelAbsorb > 0) { - HEALTH_ABSORB.renderPartial(guiGraphics, left.toFloat(), top.toFloat(), width = ceil(levelAbsorb * 80f - 0.5f)) - } - - var formattedHealth = TextComponent("%d/%d".format(ply.health.toInt(), ply.maxHealth.toInt())) - - if (ply.absorptionAmount > 0) - formattedHealth = TextComponent("%d+%d/%d".format(ply.health.toInt(), ply.absorptionAmount.toInt(), ply.maxHealth.toInt())) - - val scale = ClientConfig.HUD.BAR_TEXT_SCALE.toFloat() - guiGraphics.draw(formattedHealth, left - 2f, top + HEALTH_BG.height / 2f + 1f * scale, scale = scale, gravity = RenderGravity.CENTER_RIGHT, color = getHealthColorForPlayer(ply), drawOutline = true) - - RenderSystem.disableBlend() - RenderSystem.enableDepthTest() - } - } - - fun onLayerRenderEvent(event: RenderGuiLayerEvent.Pre) { - val gui = minecraft.gui - - if (minecraft.gameMode?.canHurtPlayer() == true && !minecraft.options.hideGui) { - if (event.name == VanillaGuiLayers.FOOD_LEVEL || event.name == VanillaGuiLayers.AIR_LEVEL) { - renderFoodAndAir(event, gui) - } else if (event.name == VanillaGuiLayers.PLAYER_HEALTH) { - renderPlayerHealth(event, gui) - } + private val registerParts = ImmutableList.of("A", "B", "C", "D", "E", "F", "M", "D", "X", "Y", "Z") } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/AndroidPackets.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/AndroidPackets.kt index eda12b649..d658b6298 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/AndroidPackets.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/AndroidPackets.kt @@ -184,11 +184,11 @@ class PlayerIterationPacket(val iteration: Int, val deathLog: List {