hud: title fixes, vehicle health element

This commit is contained in:
Bixilon 2021-10-29 11:38:53 +02:00
parent 8b6389b4c1
commit 0f0c887fa7
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
7 changed files with 194 additions and 41 deletions

View File

@ -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)
}
}

View File

@ -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

View File

@ -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<Array<HUDAtlasElement?>>()[1]?.let { ImageElement(hudRenderer, it) }
else -> selectArray.unsafeCast<Array<HUDAtlasElement?>>()[0]?.let { ImageElement(hudRenderer, it) }
}
val image = selectArray.unsafeCast<Array<HUDAtlasElement?>>()[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)
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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()
}
}

View File

@ -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

View File

@ -23,5 +23,4 @@ class TitleHideEvent(
) : PlayConnectionEvent(connection, initiator) {
constructor(connection: PlayConnection, packet: TitleHideS2CP) : this(connection, EventInitiators.SERVER)
}

View File

@ -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]
}
}
}