From 4aeb127f6b92f5791aff73fdc250820f907abdfd Mon Sep 17 00:00:00 2001 From: Bixilon Date: Fri, 12 Mar 2021 00:22:58 +0100 Subject: [PATCH] fixes, wip health bar --- .../hud/elements/other/HotbarHUDElement.kt | 33 ++++- .../elements/primitive/ElementListElement.kt | 23 ++- .../hud/elements/primitive/HealthBar.kt | 135 ++++++++++++++++++ .../hud/elements/primitive/TextElement.kt | 2 +- .../gui/rendering/textures/TextureArray.kt | 2 +- .../event/events/ChangeGameStateEvent.java | 2 +- .../events/CollectItemAnimationEvent.java | 6 +- .../java/de/bixilon/minosoft/util/MMath.kt | 21 +++ 8 files changed, 208 insertions(+), 16 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/HealthBar.kt diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/other/HotbarHUDElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/other/HotbarHUDElement.kt index 70b1bee65..afc601719 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/other/HotbarHUDElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/other/HotbarHUDElement.kt @@ -21,6 +21,7 @@ import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer import de.bixilon.minosoft.gui.rendering.hud.atlas.HUDAtlasElement import de.bixilon.minosoft.gui.rendering.hud.elements.HUDElement +import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.HealthBar import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.ImageElement import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.ProgressBar import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.TextElement @@ -28,6 +29,7 @@ import de.bixilon.minosoft.modding.event.EventInvokerCallback import de.bixilon.minosoft.modding.event.events.ChangeGameStateEvent import de.bixilon.minosoft.modding.event.events.ExperienceChangeEvent import de.bixilon.minosoft.modding.event.events.HeldItemChangeEvent +import de.bixilon.minosoft.modding.event.events.UpdateHealthEvent import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketChangeGameState import glm_.vec2.Vec2 @@ -40,6 +42,8 @@ class HotbarHUDElement( private lateinit var experienceBar: ProgressBar private lateinit var levelText: TextElement + private lateinit var healthBar: HealthBar + override fun init() { hotbarBaseAtlasElement = hudRenderer.hudAtlasElements[ResourceLocation("minecraft:hotbar_base")]!! @@ -55,6 +59,19 @@ class HotbarHUDElement( levelText = TextElement(start = Vec2(), font = hudRenderer.renderWindow.font, background = false) + healthBar = HealthBar( + Vec2(0, 0), + hudRenderer.hudAtlasElements[ResourceLocation("minecraft:black_heart_container")]!!, + hudRenderer.hudAtlasElements[ResourceLocation("minecraft:white_heart_container")]!!, + hudRenderer.hudAtlasElements[ResourceLocation("minecraft:half_red_heart")]!!, + hudRenderer.hudAtlasElements[ResourceLocation("minecraft:full_red_heart")]!!, + 20.0f, + 40.0f, + RenderConstants.HP_TEXT_COLOR, + hudRenderer.renderWindow.font, + 5 + ) + registerEvents() prepare() @@ -83,6 +100,10 @@ class HotbarHUDElement( } prepare() }) + hudRenderer.connection.registerEvent(EventInvokerCallback { + healthBar.value = it.health + prepare() + }) } @@ -97,9 +118,15 @@ class HotbarHUDElement( if (hudRenderer.connection.player.gamemode != Gamemodes.CREATIVE) { - // experience - levelText.start = Vec2((hotbarBaseAtlasElement.binding.size.x - levelText.size.x) / 2, elementList.size.y) - elementList.addChild(levelText) + healthBar.prepare() + elementList.addChild(healthBar) + + + if (hudRenderer.connection.player.level != 0) { + // experience + levelText.start = Vec2((hotbarBaseAtlasElement.binding.size.x - levelText.size.x) / 2, elementList.size.y - 3 * ELEMENT_PADDING) + elementList.addChild(levelText) + } // experience bar experienceBar.start.y = elementList.size.y - ELEMENT_PADDING diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/ElementListElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/ElementListElement.kt index f732cf7ac..b7f577049 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/ElementListElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/ElementListElement.kt @@ -27,13 +27,17 @@ open class ElementListElement( fun clear() { clearCache() - children.clear() + synchronized(children) { + children.clear() + } recalculateSize() } fun addChild(child: Element) { child.parent = this - children.add(child) + synchronized(children) { + children.add(child) + } cache.clear() recalculateSize() } @@ -50,8 +54,10 @@ open class ElementListElement( override fun clearCache() { cache.clear() - for (child in children) { - child.clearCache() + synchronized(children) { + for (child in children) { + child.clearCache() + } } } @@ -84,9 +90,12 @@ open class ElementListElement( override fun prepareCache(start: Vec2, scaleFactor: Float, matrix: Mat4, z: Int) { val normalStart = addToStart(start, this.start * scaleFactor) - for (child in children) { - child.checkCache(normalStart, scaleFactor, matrix, this.z + z) - cache.addCache(child.cache) + + synchronized(children) { + for (child in children) { + child.checkCache(normalStart, scaleFactor, matrix, this.z + z) + cache.addCache(child.cache) + } } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/HealthBar.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/HealthBar.kt new file mode 100644 index 000000000..d64e1a6fc --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/HealthBar.kt @@ -0,0 +1,135 @@ +/* + * 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.hud.elements.primitive + +import de.bixilon.minosoft.data.text.RGBColor +import de.bixilon.minosoft.data.text.TextComponent +import de.bixilon.minosoft.gui.rendering.font.Font +import de.bixilon.minosoft.gui.rendering.hud.atlas.HUDAtlasElement +import de.bixilon.minosoft.util.MMath +import glm_.mat4x4.Mat4 +import glm_.vec2.Vec2 + +class HealthBar( + start: Vec2, + var blackHeartContainerAtlasElement: HUDAtlasElement, + var whiteHeartContainerAtlasElement: HUDAtlasElement, + var halfHartAtlasElement: HUDAtlasElement, + var hartAtlasElement: HUDAtlasElement, + var maxValue: Float, + var textReplaceValue: Float, + var textColor: RGBColor, + var font: Font, + z: Int = 1, +) : ElementListElement(start, z) { + private val singleHeartSize = blackHeartContainerAtlasElement.binding.size + private val width = singleHeartSize.x * MAX_HEARTS_IN_ROW + + private val alternativeText = TextElement(font = font, start = Vec2(), background = false) + private var _value = 0.0f + var value: Float + get() = _value + set(value) { + _value = if (value < 0.0f) { + 0.0f + } else { + value + } + cache.clear() + } + + fun prepare() { + clear() + + if (value >= textReplaceValue) { + alternativeText.text = TextComponent(value.toString()).setColor(textColor) + alternativeText.start = Vec2((width - alternativeText.size.x) / 2, 0) + + addChild(alternativeText) + return + } + + val offset = Vec2(0, 0) + val containerCount = (maxValue + 1.0f).toInt() / 2 + + // heart container + val rows = MMath.divideUp(containerCount, MAX_HEARTS_IN_ROW) + for (row in 0 until rows) { + val heartsToDraw = if (row == 0 && containerCount % MAX_HEARTS_IN_ROW != 0) { + containerCount % MAX_HEARTS_IN_ROW + } else { + MAX_HEARTS_IN_ROW + } + for (i in 0 until heartsToDraw) { + drawHeart(this.start + offset, blackHeartContainerAtlasElement, z) + + offset.x += singleHeartSize.x - 1 + } + offset.y += singleHeartSize.y + offset.x = 0.0f + } + + + offset.x = 0.0f + offset.y = 0.0f + val halfHeartCount = MMath.round10Up(value) + val fullHeartCount = halfHeartCount / 2 + + val addHalfHeart = halfHeartCount % 2 == 1 + + var currentHeart = fullHeartCount - 1 + if (addHalfHeart) { + currentHeart += 1 + } + + for (row in rows - 1 downTo 0) { + val heartsInRow = if (row == 0 && containerCount % MAX_HEARTS_IN_ROW != 0) { + containerCount % MAX_HEARTS_IN_ROW + } else { + MAX_HEARTS_IN_ROW + } + for (i in 0 until heartsInRow) { + + if (currentHeart < 0) { + break + } + + if (currentHeart == 0 && addHalfHeart) { + drawHeart(this.start + offset, halfHartAtlasElement, z + 1) + } else { + drawHeart(this.start + offset, hartAtlasElement, z + 1) + } + + currentHeart-- + offset.x += singleHeartSize.x - 1 + } + offset.y += singleHeartSize.y + offset.x = 0.0f + } + } + + override fun prepareCache(start: Vec2, scaleFactor: Float, matrix: Mat4, z: Int) { + prepare() + super.prepareCache(start, scaleFactor, matrix, z) + } + + + private fun drawHeart(elementStart: Vec2, element: HUDAtlasElement, z: Int) { + addChild(ImageElement(elementStart, elementStart + singleHeartSize, element, z)) + } + + companion object { + const val MAX_HEARTS_IN_ROW = 10 + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/TextElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/TextElement.kt index 208b7216a..882f78e8c 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/TextElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/elements/primitive/TextElement.kt @@ -36,7 +36,7 @@ class TextElement( var sText: String get() = text.message set(value) { - text = ChatComponent.valueOf(sText) + text = ChatComponent.valueOf(value) } init { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt index aa833d84e..cb6f4fa6f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureArray.kt @@ -59,7 +59,7 @@ class TextureArray(val textures: MutableList) { glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST) - // glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST) // ToDo: This breaks transparency again + // glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR) // ToDo: This breaks transparency again glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, maxWidth, maxHeight, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?) diff --git a/src/main/java/de/bixilon/minosoft/modding/event/events/ChangeGameStateEvent.java b/src/main/java/de/bixilon/minosoft/modding/event/events/ChangeGameStateEvent.java index ccff1557d..9f6b8dc4b 100644 --- a/src/main/java/de/bixilon/minosoft/modding/event/events/ChangeGameStateEvent.java +++ b/src/main/java/de/bixilon/minosoft/modding/event/events/ChangeGameStateEvent.java @@ -19,7 +19,7 @@ import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketChangeGameSta /** * Fired when the player should spectate an entity */ -public class ChangeGameStateEvent extends CancelableEvent { +public class ChangeGameStateEvent extends ConnectionEvent { private final PacketChangeGameState.Reason reason; private final float value; diff --git a/src/main/java/de/bixilon/minosoft/modding/event/events/CollectItemAnimationEvent.java b/src/main/java/de/bixilon/minosoft/modding/event/events/CollectItemAnimationEvent.java index b30a696ce..ab33f1637 100644 --- a/src/main/java/de/bixilon/minosoft/modding/event/events/CollectItemAnimationEvent.java +++ b/src/main/java/de/bixilon/minosoft/modding/event/events/CollectItemAnimationEvent.java @@ -22,7 +22,7 @@ import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketCollectItem; import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_16W32A; public class CollectItemAnimationEvent extends CancelableEvent { - private final ItemEntity item; + private final Entity item; private final Entity collector; private final int count; @@ -35,12 +35,12 @@ public class CollectItemAnimationEvent extends CancelableEvent { public CollectItemAnimationEvent(Connection connection, PacketCollectItem pkg) { super(connection); - this.item = (ItemEntity) connection.getPlayer().getWorld().getEntity(pkg.getItemEntityId()); + this.item = connection.getPlayer().getWorld().getEntity(pkg.getItemEntityId()); this.collector = connection.getPlayer().getWorld().getEntity(pkg.getCollectorEntityId()); this.count = pkg.getCount(); } - public ItemEntity getItem() { + public Entity getItem() { return this.item; } diff --git a/src/main/java/de/bixilon/minosoft/util/MMath.kt b/src/main/java/de/bixilon/minosoft/util/MMath.kt index 1bc5aae46..0f6dbcf51 100644 --- a/src/main/java/de/bixilon/minosoft/util/MMath.kt +++ b/src/main/java/de/bixilon/minosoft/util/MMath.kt @@ -33,4 +33,25 @@ object MMath { } return value } + + fun divideUp(value: Int, divider: Int): Int { + return (value + divider - 1) / divider + } + + fun divideUp(value: Float, divider: Float): Float { + return (value + divider - 1.0f) / divider + } + + fun round10(value: Float): Int { + return ((value * 10).toInt() + 5) / 10 + } + + fun round10Up(value: Float): Int { + val intValue = value.toInt() + val rest = value / intValue + if (rest > 0) { + return intValue + 1 + } + return intValue + } }