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 4d0eabd97..73e5a3a36 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/MatteryGUI.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/MatteryGUI.kt @@ -21,6 +21,7 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability import ru.dbotthepony.mc.otm.capability.matteryPlayer import ru.dbotthepony.mc.otm.client.render.* +import ru.dbotthepony.mc.otm.config.ClientConfig import ru.dbotthepony.mc.otm.core.math.RGBAColor import ru.dbotthepony.mc.otm.core.util.formatPower import ru.dbotthepony.mc.otm.core.ifPresentK @@ -28,13 +29,23 @@ import java.util.* import kotlin.math.ceil object MatteryGUI { - private val BARS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/player_bars.png"), 80f, 36f) + 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, 54f) + + val HEALTH = BARS_HP.sprite(height = 9f) + val HEALTH_BG = BARS_HP.sprite(y = 9f, height = 9f) + + val HEALTH_POISON = BARS_HP.sprite(y = 18f, height = 9f) + val HEALTH_WITHER = BARS_HP.sprite(y = 27f, height = 9f) + val HEALTH_ABSORB = BARS_HP.sprite(y = 36f, height = 9f) + val HEALTH_FROZEN = BARS_HP.sprite(y = 45f, height = 9f) + private var originalBedButtonX = -1 private var originalBedButtonY = -1 @@ -97,6 +108,10 @@ object MatteryGUI { GuiOverlayManager.findOverlay(ResourceLocation("minecraft", "air_level")) } + private val PLAYER_HEALTH_ELEMENT by lazy { + GuiOverlayManager.findOverlay(ResourceLocation("minecraft", "player_health")) + } + var iteration = 0 var showIterationUntil = 0L var showIterationUntilFade = 0L @@ -175,7 +190,7 @@ object MatteryGUI { showIteration(event) } - fun onLayerRenderEvent(event: RenderGuiOverlayEvent.Pre) { + private fun renderFoodAndAir(event: RenderGuiOverlayEvent.Pre) { if (event.overlay != FOOD_LEVEL_ELEMENT && event.overlay != AIR_LEVEL_ELEMENT) { return } @@ -206,6 +221,8 @@ object MatteryGUI { return } + if (!gui.shouldDrawSurvivalElements()) return + var level: Float if (mattery.androidEnergy.maxBatteryLevel.isZero) { @@ -226,14 +243,14 @@ object MatteryGUI { val top: Int = height - gui.rightHeight gui.rightHeight += 10 - val leftPadding = ceil(level * 79f - 0.5f) + val leftPadding = ceil(level * 80f - 0.5f) if (ply.hasEffect(MobEffects.HUNGER)) { CHARGE_HUNGER_BG.render(event.poseStack, left.toFloat(), top.toFloat()) - CHARGE_HUNGER.renderPartial(event.poseStack, left.toFloat() - leftPadding + 79f, top.toFloat(), width = leftPadding) + CHARGE_HUNGER.renderPartial(event.poseStack, left.toFloat() - leftPadding + 80f, top.toFloat(), width = leftPadding) } else { CHARGE_BG.render(event.poseStack, left.toFloat(), top.toFloat()) - CHARGE.renderPartial(event.poseStack, left.toFloat() - leftPadding + 79f, top.toFloat(), width = leftPadding) + CHARGE.renderPartial(event.poseStack, left.toFloat() - leftPadding + 80f, top.toFloat(), width = leftPadding) } val formattedPower = mattery.androidEnergy.batteryLevel.formatPower() @@ -247,4 +264,87 @@ object MatteryGUI { event.poseStack.popPose() } } + + 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): Int { + if (player.hasEffect(MobEffects.POISON)) { + return RGBAColor.DARK_GREEN.toInt() + } else if (player.hasEffect(MobEffects.WITHER)) { + return RGBAColor.WHITE.toInt() + } else if (player.isFullyFrozen()) { + return RGBAColor.AQUA.toInt() + } + + return RGBAColor.RED.toInt() + } // можно вынести в конфиг, но для этого нужен селектор цвета + + private fun renderPlayerHealth(event: RenderGuiOverlayEvent.Pre) { + if (!ClientConfig.ANDROID_HEALTH_HUD) return + + val ply: LocalPlayer = minecraft.player ?: return + + var mattery = ply.matteryPlayer + + if (!ply.isAlive && mattery == null) { + mattery = lastState + } + + val gui = minecraft.gui as? ForgeGui ?: return + + if (mattery != null && mattery.isAndroid) { + event.isCanceled = true + lastState = mattery + + if (!gui.shouldDrawSurvivalElements()) return + + val level: Float = (ply.health / ply.maxHealth).coerceIn(0.0f, 1.0f) + val levelAbsorb: Float = (ply.absorptionAmount / ply.maxHealth).coerceIn(0.0f, 1.0f) + + gui.setupOverlayRenderState(true, false) + RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) + + val width = event.window.guiScaledWidth + val height = event.window.guiScaledHeight + val left = width / 2 - 10 - 81 + val top: Int = height - gui.leftHeight + gui.leftHeight += 10 + + HEALTH_BG.render(event.poseStack, left.toFloat(), top.toFloat()) + getSpriteForPlayer(ply).renderPartial(event.poseStack, left.toFloat(), top.toFloat(), width = ceil(level * 80f - 0.5f)) + if (levelAbsorb > 0) { + HEALTH_ABSORB.renderPartial(event.poseStack, left.toFloat(), top.toFloat(), width = ceil(levelAbsorb * 80f - 0.5f)) + } + + var formattedHealth = "%d/%d".format(ply.health.toInt(), ply.maxHealth.toInt()) + if (ply.absorptionAmount > 0) + formattedHealth = "%d+%d/%d".format(ply.health.toInt(), ply.absorptionAmount.toInt(), ply.maxHealth.toInt()) + + event.poseStack.pushPose() + event.poseStack.scale(0.5f, 0.5f, 0.5f) + + minecraft.font.drawAligned(event.poseStack, formattedHealth, TextAlign.CENTER_RIGHT, (left - 1f) * 2f, (top + 5.5f) * 2f, RGBAColor.BLACK.toInt()) + minecraft.font.drawAligned(event.poseStack, formattedHealth, TextAlign.CENTER_RIGHT, (left - 2f) * 2f, (top + 4.5f) * 2f, getHealthColorForPlayer(ply)) + + event.poseStack.popPose() + } + } + + fun onLayerRenderEvent(event: RenderGuiOverlayEvent.Pre) { + if (event.overlay == FOOD_LEVEL_ELEMENT || event.overlay == AIR_LEVEL_ELEMENT) { + renderFoodAndAir(event) + } else if (event.overlay == PLAYER_HEALTH_ELEMENT) { + renderPlayerHealth(event) + } + } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/config/ClientConfig.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/config/ClientConfig.kt index 4327156de..089a1ada3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/config/ClientConfig.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/config/ClientConfig.kt @@ -25,6 +25,10 @@ object ClientConfig { .comment("Allow to scroll Exopack inventory in non OTM inventories when hovering just over inventory slots, not only scrollbar") .define("EXOPACK_FREE_SCROLL", true) + var ANDROID_HEALTH_HUD: Boolean by specBuilder + .comment("Replace hearts with health bar on HUD when you are an android") + .define("ANDROID_HEALTH_HUD", true) + init { spec = specBuilder.build() } diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/player_bars.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/player_bars.png index dd0ef990b..bd9894af7 100644 Binary files a/src/main/resources/assets/overdrive_that_matters/textures/gui/player_bars.png and b/src/main/resources/assets/overdrive_that_matters/textures/gui/player_bars.png differ diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/player_bars_health.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/player_bars_health.png new file mode 100644 index 000000000..abf4b1d3a Binary files /dev/null and b/src/main/resources/assets/overdrive_that_matters/textures/gui/player_bars_health.png differ