From 0f0c887fa752ccd2e8835cf04f0c0e3c8c3271e5 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Fri, 29 Oct 2021 11:38:53 +0200 Subject: [PATCH] hud: title fixes, vehicle health element --- .../hotbar/AbstractHotbarHealthElement.kt | 49 +++++++++ .../hud/elements/hotbar/HotbarCoreElement.kt | 2 + .../elements/hotbar/HotbarHealthElement.kt | 49 +++------ .../hotbar/HotbarVehicleHealthElement.kt | 99 +++++++++++++++++++ .../gui/hud/elements/title/TitleElement.kt | 14 ++- .../event/events/title/TitleHideEvent.kt | 1 - .../assets/minosoft/mapping/atlas.json | 21 ++++ 7 files changed, 194 insertions(+), 41 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/AbstractHotbarHealthElement.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarVehicleHealthElement.kt diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/AbstractHotbarHealthElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/AbstractHotbarHealthElement.kt new file mode 100644 index 000000000..93d34511d --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/AbstractHotbarHealthElement.kt @@ -0,0 +1,49 @@ +package de.bixilon.minosoft.gui.rendering.gui.hud.elements.hotbar + +import de.bixilon.minosoft.gui.rendering.gui.elements.Element +import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ImageElement +import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer +import de.bixilon.minosoft.gui.rendering.gui.hud.atlas.HUDAtlasElement +import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer +import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions +import de.bixilon.minosoft.util.MMath.ceil +import glm_.vec2.Vec2i + +abstract class AbstractHotbarHealthElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { + abstract val totalHealth: Float + abstract val totalMaxHealth: Float + var totalMaxHearts = 0 + var rows = 0 + + + override fun forceSilentApply() { + totalMaxHearts = (totalMaxHealth / 2).ceil + + rows = totalMaxHearts / HEARTS_PER_ROW + if (totalMaxHearts % HEARTS_PER_ROW != 0) { + rows++ + } + + _size = Vec2i(HEARTS_PER_ROW, rows) * HEART_SIZE + Vec2i(1, 0) // 1 pixel is overlapping, so we have one more for the heart + cacheUpToDate = false + } + + protected fun drawCanisters(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?, atlasElement: HUDAtlasElement) { + for (heart in 0 until totalMaxHearts) { + val row = heart / HEARTS_PER_ROW + val column = heart % HEARTS_PER_ROW + + val image = ImageElement(hudRenderer, atlasElement) + + image.render(offset + Vec2i(column, (rows - 1) - row) * HEART_SIZE, z, consumer, options) + } + } + + + companion object { + const val LAYERS = 2 + private const val HP_PER_ROW = 20 + const val HEARTS_PER_ROW = HP_PER_ROW / 2 + val HEART_SIZE = Vec2i(8, 9) + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarCoreElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarCoreElement.kt index 0b392a5f7..8cabb4443 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarCoreElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarCoreElement.kt @@ -34,6 +34,7 @@ class HotbarCoreElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { val hunger = HotbarHungerElement(hudRenderer) val protection = HotbarProtectionElement(hudRenderer) val air = HotbarAirElement(hudRenderer) + val vehicleHealth = HotbarVehicleHealthElement(hudRenderer) private val topLeft = RowLayout(hudRenderer, HorizontalAlignments.LEFT, 1) // contains health, protection, etc private val topRight = RowLayout(hudRenderer, HorizontalAlignments.RIGHT, 1) // contains hunger, air @@ -67,6 +68,7 @@ class HotbarCoreElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { topRight += air topRight += hunger + topRight += vehicleHealth // non notchain, but better imho base.parent = this diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarHealthElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarHealthElement.kt index ff6cf1da3..78e4f8926 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarHealthElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarHealthElement.kt @@ -15,7 +15,6 @@ package de.bixilon.minosoft.gui.rendering.gui.hud.elements.hotbar import de.bixilon.minosoft.data.registries.effects.DefaultStatusEffects import de.bixilon.minosoft.data.registries.effects.attributes.DefaultStatusEffectAttributeNames -import de.bixilon.minosoft.gui.rendering.gui.elements.Element import de.bixilon.minosoft.gui.rendering.gui.elements.Pollable import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ImageElement import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer @@ -24,18 +23,17 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.util.KUtil.decide import de.bixilon.minosoft.util.KUtil.unsafeCast -import de.bixilon.minosoft.util.MMath.ceil import glm_.vec2.Vec2i import java.lang.Float.max import java.lang.Float.min -class HotbarHealthElement(hudRenderer: HUDRenderer) : Element(hudRenderer), Pollable { +class HotbarHealthElement(hudRenderer: HUDRenderer) : AbstractHotbarHealthElement(hudRenderer), Pollable { private val witherStatusEffect = hudRenderer.connection.registries.statusEffectRegistry[DefaultStatusEffects.WITHER] private val poisonStatusEffect = hudRenderer.connection.registries.statusEffectRegistry[DefaultStatusEffects.POISON] private val atlasManager = hudRenderer.atlasManager /** - * [normal|hardcore][normal|poison|wither][normal|damage][full|half] + * [normal|hardcore] [normal|poison|wither] [normal|damage] [full|half] */ private val hearts = arrayOf( arrayOf( @@ -140,24 +138,14 @@ class HotbarHealthElement(hudRenderer: HUDRenderer) : Element(hudRenderer), Poll private var health = 0.0f private var absorptionsAmount = 0.0f - private var totalHealth = 0.0f + override var totalHealth = 0.0f private var maxHealth = 0.0f - private var totalMaxHealth = 0.0f - - private var totalMaxHearts = 0 - private var rows = 0 + override var totalMaxHealth = 0.0f override fun forceRender(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int { // ToDo: Damage animation, regeneration, caching, stacking (and eventual text replace) - for (heart in 0 until totalMaxHearts) { - val row = heart / HEARTS_PER_ROW - val column = heart % HEARTS_PER_ROW - - val image = ImageElement(hudRenderer, blackHeartContainer) - - image.render(offset + Vec2i(column, (rows - 1) - row) * HEART_SIZE, z, consumer, options) - } + drawCanisters(offset, z, consumer, options, blackHeartContainer) val hardcoreIndex = hardcode.decide(1, 0) @@ -198,10 +186,10 @@ class HotbarHealthElement(hudRenderer: HUDRenderer) : Element(hudRenderer), Poll val halfHeart = healthLeft < 1.5f - val image = when { - halfHeart -> selectArray.unsafeCast>()[1]?.let { ImageElement(hudRenderer, it) } - else -> selectArray.unsafeCast>()[0]?.let { ImageElement(hudRenderer, it) } - } + val image = selectArray.unsafeCast>()[when { + halfHeart -> 1 + else -> 0 + }]?.let { ImageElement(hudRenderer, it) } image?.render(offset + Vec2i(column, (rows - 1) - row) * HEART_SIZE, z + 1, consumer, options) @@ -209,23 +197,14 @@ class HotbarHealthElement(hudRenderer: HUDRenderer) : Element(hudRenderer), Poll healthLeft -= halfHeart.decide(1.0f, 2.0f) } - return 2 + return LAYERS } override fun forceSilentApply() { totalHealth = health + absorptionsAmount totalMaxHealth = maxHealth + absorptionsAmount - - totalMaxHearts = (totalMaxHealth / 2).ceil - - rows = totalMaxHearts / HEARTS_PER_ROW - if (totalMaxHearts % HEARTS_PER_ROW != 0) { - rows++ - } - - _size = Vec2i(HEARTS_PER_ROW, rows) * HEART_SIZE + Vec2i(1, 0) // 1 pixel is overlapping, so we have one more for the heart - cacheUpToDate = false + super.forceSilentApply() } override fun poll(): Boolean { @@ -263,10 +242,4 @@ class HotbarHealthElement(hudRenderer: HUDRenderer) : Element(hudRenderer), Poll override fun tick() { apply() } - - companion object { - private const val HP_PER_ROW = 20 - private const val HEARTS_PER_ROW = HP_PER_ROW / 2 - private val HEART_SIZE = Vec2i(8, 9) - } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarVehicleHealthElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarVehicleHealthElement.kt new file mode 100644 index 000000000..845d29110 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/hotbar/HotbarVehicleHealthElement.kt @@ -0,0 +1,99 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.gui.hud.elements.hotbar + +import de.bixilon.minosoft.data.entities.entities.LivingEntity +import de.bixilon.minosoft.data.registries.effects.attributes.DefaultStatusEffectAttributeNames +import de.bixilon.minosoft.gui.rendering.gui.elements.Pollable +import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ImageElement +import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer +import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer +import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions +import de.bixilon.minosoft.util.KUtil.decide +import glm_.vec2.Vec2i +import java.lang.Float +import kotlin.Boolean +import kotlin.Int +import kotlin.arrayOf +import kotlin.let + +class HotbarVehicleHealthElement(hudRenderer: HUDRenderer) : AbstractHotbarHealthElement(hudRenderer), Pollable { + private val atlasManager = hudRenderer.atlasManager + + /** + * [full|half] + */ + private val hearts = arrayOf( + atlasManager["minecraft:vehicle_heart"], + atlasManager["minecraft:vehicle_half_heart"], + ) + private val vehicleHeartContainer = atlasManager["minecraft:vehicle_heart_container"]!! + + private var shown = false + override var totalHealth = 0.0f + override var totalMaxHealth = 0.0f + + override fun forceRender(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int { + // ToDo: Eventual text replace + drawCanisters(offset, z, consumer, options, vehicleHeartContainer) + + var healthLeft = totalHealth + var heart = 0 + + while (healthLeft >= 0.5f) { + val row = heart / HEARTS_PER_ROW + val column = heart % HEARTS_PER_ROW + + + val halfHeart = healthLeft < 1.5f + val image = hearts[when { + halfHeart -> 1 + else -> 0 + }]?.let { ImageElement(hudRenderer, it) } + + image?.render(offset + Vec2i(column, (rows - 1) - row) * HEART_SIZE, z + 1, consumer, options) + + heart++ + healthLeft -= halfHeart.decide(1.0f, 2.0f) + } + + return LAYERS + } + + override fun poll(): Boolean { + val riddenEntity = hudRenderer.connection.player.vehicle + if (riddenEntity == null || riddenEntity !is LivingEntity) { + if (this.shown) { + this.shown = false + return true + } + return false + } + + val health = riddenEntity.health.toFloat() + val maxHealth = Float.max(0.0f, riddenEntity.getAttributeValue(DefaultStatusEffectAttributeNames.GENERIC_MAX_HEALTH).toFloat()) + + if (health == this.totalHealth && this.totalMaxHealth == maxHealth) { + return false + } + this.totalHealth = health + this.totalMaxHealth = maxHealth + + return true + } + + override fun tick() { + apply() + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/title/TitleElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/title/TitleElement.kt index 1580a7710..31eb6f84c 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/title/TitleElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/title/TitleElement.kt @@ -26,8 +26,8 @@ import glm_.vec2.Vec2i import java.lang.Integer.max class TitleElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { - val title = FadingTextElement(hudRenderer, "", scale = 4.0f, parent = this) - val subtitle = FadingTextElement(hudRenderer, "", parent = this) + val title = FadingTextElement(hudRenderer, "", background = false, scale = 4.0f, parent = this) + val subtitle = FadingTextElement(hudRenderer, "", background = false, parent = this) var fadeInTime = 0L set(value) { title.fadeInTime = value @@ -46,6 +46,16 @@ class TitleElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { subtitle.fadeOutTime = value field = value } + override var cacheEnabled: Boolean + get() = super.cacheEnabled && title.cacheEnabled && subtitle.cacheEnabled + set(value) { + super.cacheEnabled = value + } + override var cacheUpToDate: Boolean + get() = super.cacheUpToDate && title.cacheUpToDate && subtitle.cacheUpToDate + set(value) { + super.cacheEnabled = value + } init { fadeInTime = DEFAULT_FADE_IN_TIME diff --git a/src/main/java/de/bixilon/minosoft/modding/event/events/title/TitleHideEvent.kt b/src/main/java/de/bixilon/minosoft/modding/event/events/title/TitleHideEvent.kt index fa97e3fca..db53e1044 100644 --- a/src/main/java/de/bixilon/minosoft/modding/event/events/title/TitleHideEvent.kt +++ b/src/main/java/de/bixilon/minosoft/modding/event/events/title/TitleHideEvent.kt @@ -23,5 +23,4 @@ class TitleHideEvent( ) : PlayConnectionEvent(connection, initiator) { constructor(connection: PlayConnection, packet: TitleHideS2CP) : this(connection, EventInitiators.SERVER) - } diff --git a/src/main/resources/assets/minosoft/mapping/atlas.json b/src/main/resources/assets/minosoft/mapping/atlas.json index 417b9725b..fcc623d5b 100644 --- a/src/main/resources/assets/minosoft/mapping/atlas.json +++ b/src/main/resources/assets/minosoft/mapping/atlas.json @@ -470,5 +470,26 @@ "start": [25, 18], "end": [34, 27] } + }, + "minecraft:vehicle_heart_container": { + "0": { + "texture": "minecraft:textures/gui/icons.png", + "start": [52, 9], + "end": [61, 18] + } + }, + "minecraft:vehicle_heart": { + "0": { + "texture": "minecraft:textures/gui/icons.png", + "start": [97, 9], + "end": [106, 18] + } + }, + "minecraft:vehicle_half_heart": { + "0": { + "texture": "minecraft:textures/gui/icons.png", + "start": [88, 9], + "end": [97, 18] + } } }