From 7d3689b5409fae894881b5577208138c94dc8960 Mon Sep 17 00:00:00 2001 From: olim Date: Wed, 2 Oct 2024 15:55:22 +0100 Subject: [PATCH 1/8] basic features working prototype for the feature --- .../mixins/ClientPlayNetworkHandlerMixin.java | 2 + .../hysky/skyblocker/skyblock/HealthBars.java | 111 ++++++++++++++++++ .../skyblocker/utils/render/RenderHelper.java | 30 +++++ 3 files changed, 143 insertions(+) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index fe35e7d2d6..c2b95200b5 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -7,6 +7,7 @@ import de.hysky.skyblocker.config.configs.SlayersConfig; import de.hysky.skyblocker.skyblock.CompactDamage; import de.hysky.skyblocker.skyblock.FishingHelper; +import de.hysky.skyblocker.skyblock.HealthBars; import de.hysky.skyblocker.skyblock.chocolatefactory.EggFinder; import de.hysky.skyblocker.skyblock.crimson.dojo.DojoManager; import de.hysky.skyblocker.skyblock.crimson.slayer.FirePillarAnnouncer; @@ -126,6 +127,7 @@ public abstract class ClientPlayNetworkHandlerMixin { EggFinder.checkIfEgg(armorStandEntity); try { //Prevent packet handling fails if something goes wrong so that entity trackers still update, just without compact damage numbers CompactDamage.compactDamage(armorStandEntity); + HealthBars.HeathBar(armorStandEntity); } catch (Exception e) { LOGGER.error("[Skyblocker Compact Damage] Failed to compact damage number", e); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java new file mode 100644 index 0000000000..efb23235d0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java @@ -0,0 +1,111 @@ +package de.hysky.skyblocker.skyblock; + +import de.hysky.skyblocker.annotations.Init; +import de.hysky.skyblocker.utils.render.RenderHelper; +import it.unimi.dsi.fastutil.objects.Object2FloatMap; +import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.Vec3d; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class HealthBars { + + private static final Identifier HEALTH_BAR_BACKGROUND_TEXTURE = Identifier.ofVanilla("textures/gui/sprites/boss_bar/purple_background.png"); + private static final Identifier HEALTH_BAR_TEXTURE = Identifier.ofVanilla("textures/gui/sprites/boss_bar/purple_progress.png"); + private static final Pattern HEALTH_PATTERN = Pattern.compile("(\\d{1,3}(,\\d{3})*(\\.\\d+)?)/(\\d{1,3}(,\\d{3})*(\\.\\d+)?)❤"); + + private static Object2FloatOpenHashMap healthValues = new Object2FloatOpenHashMap<>(); + + @Init + public static void init() { + ClientPlayConnectionEvents.JOIN.register((_handler, _sender, _client) -> reset()); + WorldRenderEvents.AFTER_TRANSLUCENT.register(HealthBars::render); + ClientEntityEvents.ENTITY_UNLOAD.register(HealthBars::onEntityDespawn); + } + + private static void reset() { + healthValues.clear(); + } + + /** + * remove dead armour stands from health bars + * + * @param entity dying entity + */ + public static void onEntityDespawn(Entity entity, ClientWorld clientWorld) { + if (entity instanceof ArmorStandEntity armorStandEntity) { + healthValues.removeFloat(armorStandEntity); + } + } + + public static void HeathBar(ArmorStandEntity armorStand) { + //todo return based on enabled + if (!armorStand.isInvisible() || !armorStand.hasCustomName() || !armorStand.isCustomNameVisible()) { + return; + } + + //check if armour stand is dead and remove it from list + if (armorStand.isDead()) { + healthValues.removeFloat(armorStand); + return; + } + + //check to see if the armour stand is a mob label with health + + Matcher healthMatcher = HEALTH_PATTERN.matcher(armorStand.getCustomName().getString()); + if (!healthMatcher.find()) { + return; + } + + //work out health value and save to hashMap + System.out.println(healthMatcher.group(1)); + int firstValue = Integer.parseInt(healthMatcher.group(1).replace(",","")); + int secondValue = Integer.parseInt(healthMatcher.group(4).replace(",","")); + float health = (float) firstValue / secondValue; + healthValues.put(armorStand, health); + + //edit armour stand name to remove health todo if enabled or only show total and not max + MutableText cleanedText = Text.empty(); + List parts = armorStand.getCustomName().getSiblings(); + for (int i = 0; i < parts.size() - 3; i++) { //todo is health always at the end or do i need to add after that + //found health remove stop adding + if (parts.get(i).getString().equals(healthMatcher.group(1)) && parts.get(i + 1).getString().equals("/") && parts.get(i + 2).getString().equals(healthMatcher.group(4)) && parts.get(i + 3).getString().equals("❤")) { + break; + } + cleanedText.append(parts.get(i)); + } + armorStand.setCustomName(cleanedText); + + } + + /** + * Loops though armor stands with health bars and renders a bar for each of them just bellow + * + * @param context render context + */ + private static void render(WorldRenderContext context) { + + for (Object2FloatMap.Entry healthValue : healthValues.object2FloatEntrySet()) { + ArmorStandEntity armorStand = healthValue.getKey(); + + // Render the health bar texture with scaling based on health percentage + RenderHelper.renderTextureQuad(context, armorStand.getCameraPosVec(context.tickCounter().getTickDelta(false)).add(0, 0.15, 0), 1f, 0.1f, 1f, 1f, new Vec3d(-0.5f, 0, 0), HEALTH_BAR_BACKGROUND_TEXTURE, false); + RenderHelper.renderTextureQuad(context, armorStand.getCameraPosVec(context.tickCounter().getTickDelta(false)).add(0, 0.15, 0), healthValue.getFloatValue(), 0.1f, healthValue.getFloatValue(), 1f, new Vec3d(-0.5f, 0, 0.003f), HEALTH_BAR_TEXTURE, false); + + } + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java index 8980f3482a..021cee521f 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java @@ -284,6 +284,36 @@ public static void renderQuad(WorldRenderContext context, Vec3d[] points, float[ RenderSystem.enableCull(); RenderSystem.depthFunc(GL11.GL_LEQUAL); } + + public static void renderTextureQuad(WorldRenderContext context, Vec3d pos, float width, float height, float textureWidth, float textureHeight, Vec3d renderOffset, Identifier texture, boolean throughWalls) { + Matrix4f positionMatrix = new Matrix4f(); + Camera camera = context.camera(); + Vec3d cameraPos = camera.getPos(); + + positionMatrix + .translate((float) (pos.getX() - cameraPos.getX()), (float) (pos.getY() - cameraPos.getY()), (float) (pos.getZ() - cameraPos.getZ())) + .rotate(camera.getRotation()); + + Tessellator tessellator = RenderSystem.renderThreadTesselator(); + + RenderSystem.setShaderTexture(0, texture); + RenderSystem.setShader(GameRenderer::getPositionTexProgram); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.disableCull(); + RenderSystem.depthFunc(throughWalls ? GL11.GL_ALWAYS : GL11.GL_LEQUAL); + BufferBuilder buffer = tessellator.begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE); + + buffer.vertex(positionMatrix, (float) renderOffset.getX(), (float) renderOffset.getY(), (float) renderOffset.getZ()).texture(1, 1 - textureHeight); + buffer.vertex(positionMatrix, (float) renderOffset.getX(), (float) renderOffset.getY() + height, (float) renderOffset.getZ()).texture(1, 1); + buffer.vertex(positionMatrix, (float) renderOffset.getX() + width, (float) renderOffset.getY() + height , (float) renderOffset.getZ()).texture(1 - textureWidth, 1); + buffer.vertex(positionMatrix, (float) renderOffset.getX() + width, (float) renderOffset.getY(), (float) renderOffset.getZ()).texture(1 - textureWidth, 1 - textureHeight); + + BufferRenderer.drawWithGlobalProgram(buffer.end()); + + RenderSystem.enableCull(); + RenderSystem.depthFunc(GL11.GL_LEQUAL); + } public static void renderText(WorldRenderContext context, Text text, Vec3d pos, boolean throughWalls) { renderText(context, text, pos, 1, throughWalls); From f5a91f26dd38e87b0e052c153f177fd4e2b3f434 Mon Sep 17 00:00:00 2001 From: olim Date: Sun, 6 Oct 2024 12:17:11 +0100 Subject: [PATCH 2/8] add config and main features --- .../categories/UIAndVisualsCategory.java | 49 +++++++++++++ .../config/configs/UIAndVisualsConfig.java | 23 ++++++ .../hysky/skyblocker/skyblock/HealthBars.java | 70 +++++++++++++------ .../skyblocker/utils/render/RenderHelper.java | 4 +- .../assets/skyblocker/lang/en_us.json | 8 +++ 5 files changed, 131 insertions(+), 23 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java index fa9bf37648..26e833908c 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java @@ -437,6 +437,55 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .build() ) + //Custom Health bars + .group(OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.enabled")) + .binding(defaults.uiAndVisuals.healthBars.enabled, + () -> config.uiAndVisuals.healthBars.enabled, + newValue -> config.uiAndVisuals.healthBars.enabled = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.scale")) + .binding(defaults.uiAndVisuals.healthBars.scale, + () -> config.uiAndVisuals.healthBars.scale, + newValue -> config.uiAndVisuals.healthBars.scale = newValue) + .controller(FloatFieldControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.removeHealthFromName")) + .binding(defaults.uiAndVisuals.healthBars.removeHealthFromName, + () -> config.uiAndVisuals.healthBars.removeHealthFromName, + newValue -> config.uiAndVisuals.healthBars.removeHealthFromName = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.removeMaxHealthFromName")) + .binding(defaults.uiAndVisuals.healthBars.removeMaxHealthFromName, + () -> config.uiAndVisuals.healthBars.removeMaxHealthFromName, + newValue -> config.uiAndVisuals.healthBars.removeMaxHealthFromName = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.hideFullHealth")) + .binding(defaults.uiAndVisuals.healthBars.hideFullHealth, + () -> config.uiAndVisuals.healthBars.hideFullHealth, + newValue -> config.uiAndVisuals.healthBars.hideFullHealth = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.barColor")) + .binding(defaults.uiAndVisuals.healthBars.barColor, + () -> config.uiAndVisuals.healthBars.barColor, + newValue -> config.uiAndVisuals.healthBars.barColor = newValue) + .controller(ColorControllerBuilder::create) + .build()) + .build() + ) + .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java index 598cbf5498..df7c6ce734 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java @@ -67,6 +67,9 @@ public class UIAndVisualsConfig { @SerialEntry public CompactDamage compactDamage = new CompactDamage(); + @SerialEntry + public HealthBars healthBars = new HealthBars(); + public static class ChestValue { @SerialEntry public boolean enableChestValue = true; @@ -281,4 +284,24 @@ public static class CompactDamage { @SerialEntry public Color critDamageGradientEnd = new Color(0xFF5555); } + + public static class HealthBars { + @SerialEntry + public boolean enabled = true; + + @SerialEntry + public float scale = 1f; + + @SerialEntry + public boolean removeHealthFromName = true; + + @SerialEntry + public boolean removeMaxHealthFromName = true; + + @SerialEntry + public boolean hideFullHealth = true; + + @SerialEntry + public Color barColor = new Color(0xFFFFFF); + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java index efb23235d0..84a8a4ec7a 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java @@ -1,6 +1,7 @@ package de.hysky.skyblocker.skyblock; import de.hysky.skyblocker.annotations.Init; +import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.utils.render.RenderHelper; import it.unimi.dsi.fastutil.objects.Object2FloatMap; import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; @@ -16,17 +17,18 @@ import net.minecraft.util.Identifier; import net.minecraft.util.math.Vec3d; +import java.awt.*; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class HealthBars { - private static final Identifier HEALTH_BAR_BACKGROUND_TEXTURE = Identifier.ofVanilla("textures/gui/sprites/boss_bar/purple_background.png"); - private static final Identifier HEALTH_BAR_TEXTURE = Identifier.ofVanilla("textures/gui/sprites/boss_bar/purple_progress.png"); + private static final Identifier HEALTH_BAR_BACKGROUND_TEXTURE = Identifier.ofVanilla("textures/gui/sprites/boss_bar/white_background.png"); + private static final Identifier HEALTH_BAR_TEXTURE = Identifier.ofVanilla("textures/gui/sprites/boss_bar/white_progress.png"); private static final Pattern HEALTH_PATTERN = Pattern.compile("(\\d{1,3}(,\\d{3})*(\\.\\d+)?)/(\\d{1,3}(,\\d{3})*(\\.\\d+)?)❤"); - private static Object2FloatOpenHashMap healthValues = new Object2FloatOpenHashMap<>(); + private static final Object2FloatOpenHashMap healthValues = new Object2FloatOpenHashMap<>(); @Init public static void init() { @@ -51,8 +53,7 @@ public static void onEntityDespawn(Entity entity, ClientWorld clientWorld) { } public static void HeathBar(ArmorStandEntity armorStand) { - //todo return based on enabled - if (!armorStand.isInvisible() || !armorStand.hasCustomName() || !armorStand.isCustomNameVisible()) { + if (!armorStand.isInvisible() || !armorStand.hasCustomName() || !armorStand.isCustomNameVisible() || !SkyblockerConfigManager.get().uiAndVisuals.healthBars.enabled) { return; } @@ -63,7 +64,9 @@ public static void HeathBar(ArmorStandEntity armorStand) { } //check to see if the armour stand is a mob label with health - + if (armorStand.getCustomName() == null) { + return; + } Matcher healthMatcher = HEALTH_PATTERN.matcher(armorStand.getCustomName().getString()); if (!healthMatcher.find()) { return; @@ -71,41 +74,64 @@ public static void HeathBar(ArmorStandEntity armorStand) { //work out health value and save to hashMap System.out.println(healthMatcher.group(1)); - int firstValue = Integer.parseInt(healthMatcher.group(1).replace(",","")); - int secondValue = Integer.parseInt(healthMatcher.group(4).replace(",","")); + int firstValue = Integer.parseInt(healthMatcher.group(1).replace(",", "")); + int secondValue = Integer.parseInt(healthMatcher.group(4).replace(",", "")); float health = (float) firstValue / secondValue; healthValues.put(armorStand, health); - //edit armour stand name to remove health todo if enabled or only show total and not max + //edit armour stand name to remove health + boolean removeValue = SkyblockerConfigManager.get().uiAndVisuals.healthBars.removeHealthFromName; + boolean removeMax = SkyblockerConfigManager.get().uiAndVisuals.healthBars.removeMaxHealthFromName; + //if both disabled no need to edit name + if (!removeValue && !removeMax) { + return; + } MutableText cleanedText = Text.empty(); List parts = armorStand.getCustomName().getSiblings(); - for (int i = 0; i < parts.size() - 3; i++) { //todo is health always at the end or do i need to add after that - //found health remove stop adding - if (parts.get(i).getString().equals(healthMatcher.group(1)) && parts.get(i + 1).getString().equals("/") && parts.get(i + 2).getString().equals(healthMatcher.group(4)) && parts.get(i + 3).getString().equals("❤")) { - break; + for (int i = 0; i < parts.size(); i++) { + //remove value from name + if (removeValue && i < parts.size() - 3 && parts.get(i).getString().equals(healthMatcher.group(1)) && parts.get(i + 1).getString().equals("/") && parts.get(i + 2).getString().equals(healthMatcher.group(4)) && parts.get(i + 3).getString().equals("❤")) { + continue; + } + //remove slash from max + if (removeMax && i < parts.size() - 2 && parts.get(i).getString().equals("/") && parts.get(i + 1).getString().equals(healthMatcher.group(4)) && parts.get(i + 2).getString().equals("❤")) { + continue; + } + //remove max + if (removeMax && i < parts.size() - 1 && parts.get(i).getString().equals(healthMatcher.group(4)) && parts.get(i + 1).getString().equals("❤")) { + continue; + } + //if both enabled remove "❤" + if (removeValue && removeMax && parts.get(i).getString().equals("❤")) { + continue; } cleanedText.append(parts.get(i)); } armorStand.setCustomName(cleanedText); - } /** - * Loops though armor stands with health bars and renders a bar for each of them just bellow + * Loops though armor stands with health bars and renders a bar for each of them just bellow the name label * * @param context render context */ private static void render(WorldRenderContext context) { - + if (!SkyblockerConfigManager.get().uiAndVisuals.healthBars.enabled || healthValues.isEmpty()) { + return; + } + Color barColor = SkyblockerConfigManager.get().uiAndVisuals.healthBars.barColor; + boolean hideFullHealth = SkyblockerConfigManager.get().uiAndVisuals.healthBars.hideFullHealth; + float scale = SkyblockerConfigManager.get().uiAndVisuals.healthBars.scale; for (Object2FloatMap.Entry healthValue : healthValues.object2FloatEntrySet()) { - ArmorStandEntity armorStand = healthValue.getKey(); + //if the health bar is full and the setting is enabled to hide it stop rendering it + if (hideFullHealth && healthValue.getFloatValue() == 1) { + continue; + } + ArmorStandEntity armorStand = healthValue.getKey(); // Render the health bar texture with scaling based on health percentage - RenderHelper.renderTextureQuad(context, armorStand.getCameraPosVec(context.tickCounter().getTickDelta(false)).add(0, 0.15, 0), 1f, 0.1f, 1f, 1f, new Vec3d(-0.5f, 0, 0), HEALTH_BAR_BACKGROUND_TEXTURE, false); - RenderHelper.renderTextureQuad(context, armorStand.getCameraPosVec(context.tickCounter().getTickDelta(false)).add(0, 0.15, 0), healthValue.getFloatValue(), 0.1f, healthValue.getFloatValue(), 1f, new Vec3d(-0.5f, 0, 0.003f), HEALTH_BAR_TEXTURE, false); - + RenderHelper.renderTextureQuad(context, armorStand.getCameraPosVec(context.tickCounter().getTickDelta(false)).add(0, 0.25 - (0.1f * scale), 0), scale, 0.1f * scale, 1f, 1f, new Vec3d(-0.5f * scale, 0, 0), HEALTH_BAR_BACKGROUND_TEXTURE, barColor, false); + RenderHelper.renderTextureQuad(context, armorStand.getCameraPosVec(context.tickCounter().getTickDelta(false)).add(0, 0.25 - (0.1f * scale), 0), healthValue.getFloatValue() * scale, 0.1f * scale, healthValue.getFloatValue(), 1f, new Vec3d(-0.5f * scale, 0, 0.003f), HEALTH_BAR_TEXTURE, barColor, false); } - } - } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java index 021cee521f..655e5e707b 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java @@ -285,7 +285,7 @@ public static void renderQuad(WorldRenderContext context, Vec3d[] points, float[ RenderSystem.depthFunc(GL11.GL_LEQUAL); } - public static void renderTextureQuad(WorldRenderContext context, Vec3d pos, float width, float height, float textureWidth, float textureHeight, Vec3d renderOffset, Identifier texture, boolean throughWalls) { + public static void renderTextureQuad(WorldRenderContext context, Vec3d pos, float width, float height, float textureWidth, float textureHeight, Vec3d renderOffset, Identifier texture, Color shaderColor, boolean throughWalls) { Matrix4f positionMatrix = new Matrix4f(); Camera camera = context.camera(); Vec3d cameraPos = camera.getPos(); @@ -297,6 +297,7 @@ public static void renderTextureQuad(WorldRenderContext context, Vec3d pos, floa Tessellator tessellator = RenderSystem.renderThreadTesselator(); RenderSystem.setShaderTexture(0, texture); + RenderSystem.setShaderColor(shaderColor.getRed() / 255f, shaderColor.getGreen() / 255f, shaderColor.getBlue() / 255f, shaderColor.getAlpha() / 255f); RenderSystem.setShader(GameRenderer::getPositionTexProgram); RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); @@ -311,6 +312,7 @@ public static void renderTextureQuad(WorldRenderContext context, Vec3d pos, floa BufferRenderer.drawWithGlobalProgram(buffer.end()); + RenderSystem.setShaderColor(1f, 1f, 1f, 1f); RenderSystem.enableCull(); RenderSystem.depthFunc(GL11.GL_LEQUAL); } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index eea3fd24b0..be2121fffa 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -752,6 +752,14 @@ "skyblocker.config.uiAndVisuals.hideEmptyTooltips": "Hide empty item tooltips in menus", "skyblocker.config.uiAndVisuals.hideEmptyTooltips.@Tooltip": "Hides the tooltip of an item if it doesn't have any information to display. Also blocks clicks on filler items like glass panes.", + "skyblocker.config.uiAndVisuals.healthBars": "Custom Mob Health Bars", + "skyblocker.config.uiAndVisuals.healthBars.enabled": "Enabled", + "skyblocker.config.uiAndVisuals.healthBars.scale": "Scale", + "skyblocker.config.uiAndVisuals.healthBars.removeHealthFromName": "Remove Health From Name", + "skyblocker.config.uiAndVisuals.healthBars.removeMaxHealthFromName": "Remove Max Health From Name", + "skyblocker.config.uiAndVisuals.healthBars.hideFullHealth": "Hide Full Health Bars", + "skyblocker.config.uiAndVisuals.healthBars.barColor": "Health Bar Color", + "skyblocker.config.uiAndVisuals.inputCalculator": "Input Calculator", "skyblocker.config.uiAndVisuals.inputCalculator.enabled": "Enable Sign Calculator", "skyblocker.config.uiAndVisuals.inputCalculator.enabled.@Tooltip": "Enables the ability for you to do calculations when inputting values such as price for the ah.\n Key:\n S = 64\n E = 160\n K = 1,000\n M = 1,000,000\n B = 1,000,000,000\n\n purse/P = current purse value", From e9aea10e5ed82190ad00c6e521fd71c118751b0e Mon Sep 17 00:00:00 2001 From: olim Date: Sun, 6 Oct 2024 13:01:57 +0100 Subject: [PATCH 3/8] add option to show on mobs with only a health value also adds tooltips for options --- .../categories/UIAndVisualsCategory.java | 11 ++++ .../config/configs/UIAndVisualsConfig.java | 5 +- .../hysky/skyblocker/skyblock/HealthBars.java | 55 ++++++++++++++++++- .../assets/skyblocker/lang/en_us.json | 5 ++ 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java index 26e833908c..6326180569 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java @@ -457,6 +457,7 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .build()) .option(Option.createBuilder() .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.removeHealthFromName")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.removeHealthFromName.@Tooltip"))) .binding(defaults.uiAndVisuals.healthBars.removeHealthFromName, () -> config.uiAndVisuals.healthBars.removeHealthFromName, newValue -> config.uiAndVisuals.healthBars.removeHealthFromName = newValue) @@ -464,13 +465,23 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .build()) .option(Option.createBuilder() .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.removeMaxHealthFromName")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.removeMaxHealthFromName.@Tooltip"))) .binding(defaults.uiAndVisuals.healthBars.removeMaxHealthFromName, () -> config.uiAndVisuals.healthBars.removeMaxHealthFromName, newValue -> config.uiAndVisuals.healthBars.removeMaxHealthFromName = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.applyToHealthOnlyMobs")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.applyToHealthOnlyMobs.@Tooltip"))) + .binding(defaults.uiAndVisuals.healthBars.applyToHealthOnlyMobs, + () -> config.uiAndVisuals.healthBars.applyToHealthOnlyMobs, + newValue -> config.uiAndVisuals.healthBars.applyToHealthOnlyMobs = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) .option(Option.createBuilder() .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.hideFullHealth")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.hideFullHealth.@Tooltip"))) .binding(defaults.uiAndVisuals.healthBars.hideFullHealth, () -> config.uiAndVisuals.healthBars.hideFullHealth, newValue -> config.uiAndVisuals.healthBars.hideFullHealth = newValue) diff --git a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java index df7c6ce734..5e6d8a9f83 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java @@ -299,7 +299,10 @@ public static class HealthBars { public boolean removeMaxHealthFromName = true; @SerialEntry - public boolean hideFullHealth = true; + public boolean applyToHealthOnlyMobs = true; + + @SerialEntry + public boolean hideFullHealth = false; @SerialEntry public Color barColor = new Color(0xFFFFFF); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java index 84a8a4ec7a..087420ce6d 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java @@ -5,6 +5,7 @@ import de.hysky.skyblocker.utils.render.RenderHelper; import it.unimi.dsi.fastutil.objects.Object2FloatMap; import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; @@ -27,8 +28,10 @@ public class HealthBars { private static final Identifier HEALTH_BAR_BACKGROUND_TEXTURE = Identifier.ofVanilla("textures/gui/sprites/boss_bar/white_background.png"); private static final Identifier HEALTH_BAR_TEXTURE = Identifier.ofVanilla("textures/gui/sprites/boss_bar/white_progress.png"); private static final Pattern HEALTH_PATTERN = Pattern.compile("(\\d{1,3}(,\\d{3})*(\\.\\d+)?)/(\\d{1,3}(,\\d{3})*(\\.\\d+)?)❤"); + private static final Pattern HEALTH_ONLY_PATTERN = Pattern.compile("(\\d{1,3}(,\\d{3})*(\\.\\d+)?)❤"); private static final Object2FloatOpenHashMap healthValues = new Object2FloatOpenHashMap<>(); + private static final Object2IntOpenHashMap mobStartingHealth = new Object2IntOpenHashMap<>(); @Init public static void init() { @@ -39,6 +42,7 @@ public static void init() { private static void reset() { healthValues.clear(); + mobStartingHealth.clear(); } /** @@ -49,6 +53,7 @@ private static void reset() { public static void onEntityDespawn(Entity entity, ClientWorld clientWorld) { if (entity instanceof ArmorStandEntity armorStandEntity) { healthValues.removeFloat(armorStandEntity); + mobStartingHealth.removeInt(armorStandEntity); } } @@ -59,7 +64,10 @@ public static void HeathBar(ArmorStandEntity armorStand) { //check if armour stand is dead and remove it from list if (armorStand.isDead()) { + System.out.println("somthing"); healthValues.removeFloat(armorStand); + mobStartingHealth.removeInt(armorStand); + return; } @@ -68,12 +76,13 @@ public static void HeathBar(ArmorStandEntity armorStand) { return; } Matcher healthMatcher = HEALTH_PATTERN.matcher(armorStand.getCustomName().getString()); + //if a health ratio can not be found send onto health only pattern if (!healthMatcher.find()) { + HealthOnlyCheck(armorStand); return; } //work out health value and save to hashMap - System.out.println(healthMatcher.group(1)); int firstValue = Integer.parseInt(healthMatcher.group(1).replace(",", "")); int secondValue = Integer.parseInt(healthMatcher.group(4).replace(",", "")); float health = (float) firstValue / secondValue; @@ -110,6 +119,50 @@ public static void HeathBar(ArmorStandEntity armorStand) { armorStand.setCustomName(cleanedText); } + private static void HealthOnlyCheck(ArmorStandEntity armorStand) { + //todo setting for this + if (!SkyblockerConfigManager.get().uiAndVisuals.healthBars.applyToHealthOnlyMobs || armorStand.getCustomName() == null) { + return; + } + Matcher healthOnlyMatcher = HEALTH_ONLY_PATTERN.matcher(armorStand.getCustomName().getString()); + //if not found return + if (!healthOnlyMatcher.find()) { + return; + } + + //get the current health of the mob + int currentHealth = Integer.parseInt(healthOnlyMatcher.group(1).replace(",", "")); + + //if it's a new health only armor stand add to starting health lookup (not always full health if already damaged but best that can be done) + if (!mobStartingHealth.containsKey(armorStand)) { + mobStartingHealth.put(armorStand,currentHealth); + } + + //add to health bar values + float health = (float) currentHealth / mobStartingHealth.getInt(armorStand); + healthValues.put(armorStand, health); + + //if enabled remove from name + if (!SkyblockerConfigManager.get().uiAndVisuals.healthBars.removeHealthFromName) { + return; + } + MutableText cleanedText = Text.empty(); + List parts = armorStand.getCustomName().getSiblings(); + for (int i = 0; i < parts.size(); i++) { + //remove value from name + if (i < parts.size() - 1 && parts.get(i).getString().equals(healthOnlyMatcher.group(1)) && parts.get(i + 1).getString().equals("❤")) { + continue; + } + + //remove "❤" + if (parts.get(i).getString().equals("❤")) { + continue; + } + cleanedText.append(parts.get(i)); + } + armorStand.setCustomName(cleanedText); + } + /** * Loops though armor stands with health bars and renders a bar for each of them just bellow the name label * diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index be2121fffa..9315dd6436 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -756,8 +756,13 @@ "skyblocker.config.uiAndVisuals.healthBars.enabled": "Enabled", "skyblocker.config.uiAndVisuals.healthBars.scale": "Scale", "skyblocker.config.uiAndVisuals.healthBars.removeHealthFromName": "Remove Health From Name", + "skyblocker.config.uiAndVisuals.healthBars.removeHealthFromName.@Tooltip": "Remove the current health of mobs from their nametag", "skyblocker.config.uiAndVisuals.healthBars.removeMaxHealthFromName": "Remove Max Health From Name", + "skyblocker.config.uiAndVisuals.healthBars.removeMaxHealthFromName.@Tooltip": "Remove the maximum health of mobs from their nametag", + "skyblocker.config.uiAndVisuals.healthBars.applyToHealthOnlyMobs": "Apply to Health Only Mobs", + "skyblocker.config.uiAndVisuals.healthBars.applyToHealthOnlyMobs.@Tooltip": "Also add health bars to mobs with only a current health value (will sometimes be wrong if mob is damaged before loading in)", "skyblocker.config.uiAndVisuals.healthBars.hideFullHealth": "Hide Full Health Bars", + "skyblocker.config.uiAndVisuals.healthBars.hideFullHealth.@Tooltip": "Don't show the health bar when its full", "skyblocker.config.uiAndVisuals.healthBars.barColor": "Health Bar Color", "skyblocker.config.uiAndVisuals.inputCalculator": "Input Calculator", From 1b33cb76b08c34bd0fc7fc5b2cf7b511c1a6afc5 Mon Sep 17 00:00:00 2001 From: olim88 Date: Thu, 17 Oct 2024 18:57:03 +0100 Subject: [PATCH 4/8] clean up code and add more comments --- .../hysky/skyblocker/skyblock/HealthBars.java | 45 +++++++++++-------- .../skyblocker/utils/render/RenderHelper.java | 15 ++++++- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java index 087420ce6d..9929f9b43e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java @@ -57,6 +57,11 @@ public static void onEntityDespawn(Entity entity, ClientWorld clientWorld) { } } + /** + * Processes armourtand updates and if it's a mob with health get the value of its health and save it the hashmap + * + * @param armorStand updated armourstand + */ public static void HeathBar(ArmorStandEntity armorStand) { if (!armorStand.isInvisible() || !armorStand.hasCustomName() || !armorStand.isCustomNameVisible() || !SkyblockerConfigManager.get().uiAndVisuals.healthBars.enabled) { return; @@ -64,10 +69,8 @@ public static void HeathBar(ArmorStandEntity armorStand) { //check if armour stand is dead and remove it from list if (armorStand.isDead()) { - System.out.println("somthing"); healthValues.removeFloat(armorStand); mobStartingHealth.removeInt(armorStand); - return; } @@ -102,12 +105,10 @@ public static void HeathBar(ArmorStandEntity armorStand) { if (removeValue && i < parts.size() - 3 && parts.get(i).getString().equals(healthMatcher.group(1)) && parts.get(i + 1).getString().equals("/") && parts.get(i + 2).getString().equals(healthMatcher.group(4)) && parts.get(i + 3).getString().equals("❤")) { continue; } - //remove slash from max + //remove slash from max and skip over the value if (removeMax && i < parts.size() - 2 && parts.get(i).getString().equals("/") && parts.get(i + 1).getString().equals(healthMatcher.group(4)) && parts.get(i + 2).getString().equals("❤")) { - continue; - } - //remove max - if (removeMax && i < parts.size() - 1 && parts.get(i).getString().equals(healthMatcher.group(4)) && parts.get(i + 1).getString().equals("❤")) { + //remove the value as well + i += 1; continue; } //if both enabled remove "❤" @@ -119,9 +120,13 @@ public static void HeathBar(ArmorStandEntity armorStand) { armorStand.setCustomName(cleanedText); } + /** + * Processes armour stands that only have a health value and no max health + * + * @param armorStand armorstand to check the name of + */ private static void HealthOnlyCheck(ArmorStandEntity armorStand) { - //todo setting for this - if (!SkyblockerConfigManager.get().uiAndVisuals.healthBars.applyToHealthOnlyMobs || armorStand.getCustomName() == null) { + if (!SkyblockerConfigManager.get().uiAndVisuals.healthBars.applyToHealthOnlyMobs || armorStand.getCustomName() == null) { return; } Matcher healthOnlyMatcher = HEALTH_ONLY_PATTERN.matcher(armorStand.getCustomName().getString()); @@ -135,7 +140,7 @@ private static void HealthOnlyCheck(ArmorStandEntity armorStand) { //if it's a new health only armor stand add to starting health lookup (not always full health if already damaged but best that can be done) if (!mobStartingHealth.containsKey(armorStand)) { - mobStartingHealth.put(armorStand,currentHealth); + mobStartingHealth.put(armorStand, currentHealth); } //add to health bar values @@ -149,13 +154,10 @@ private static void HealthOnlyCheck(ArmorStandEntity armorStand) { MutableText cleanedText = Text.empty(); List parts = armorStand.getCustomName().getSiblings(); for (int i = 0; i < parts.size(); i++) { - //remove value from name + //remove value from name and skip over heart if (i < parts.size() - 1 && parts.get(i).getString().equals(healthOnlyMatcher.group(1)) && parts.get(i + 1).getString().equals("❤")) { - continue; - } - - //remove "❤" - if (parts.get(i).getString().equals("❤")) { + //skip the heart + i += 1; continue; } cleanedText.append(parts.get(i)); @@ -175,16 +177,21 @@ private static void render(WorldRenderContext context) { Color barColor = SkyblockerConfigManager.get().uiAndVisuals.healthBars.barColor; boolean hideFullHealth = SkyblockerConfigManager.get().uiAndVisuals.healthBars.hideFullHealth; float scale = SkyblockerConfigManager.get().uiAndVisuals.healthBars.scale; + float tickDelta = context.tickCounter().getTickDelta(false); + for (Object2FloatMap.Entry healthValue : healthValues.object2FloatEntrySet()) { //if the health bar is full and the setting is enabled to hide it stop rendering it - if (hideFullHealth && healthValue.getFloatValue() == 1) { + float health = healthValue.getFloatValue(); + if (hideFullHealth && health == 1) { continue; } ArmorStandEntity armorStand = healthValue.getKey(); // Render the health bar texture with scaling based on health percentage - RenderHelper.renderTextureQuad(context, armorStand.getCameraPosVec(context.tickCounter().getTickDelta(false)).add(0, 0.25 - (0.1f * scale), 0), scale, 0.1f * scale, 1f, 1f, new Vec3d(-0.5f * scale, 0, 0), HEALTH_BAR_BACKGROUND_TEXTURE, barColor, false); - RenderHelper.renderTextureQuad(context, armorStand.getCameraPosVec(context.tickCounter().getTickDelta(false)).add(0, 0.25 - (0.1f * scale), 0), healthValue.getFloatValue() * scale, 0.1f * scale, healthValue.getFloatValue(), 1f, new Vec3d(-0.5f * scale, 0, 0.003f), HEALTH_BAR_TEXTURE, barColor, false); + float width = scale; + float height = scale * 0.1f; + RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width, height, 1f, 1f, new Vec3d(width * -0.5f, 0, 0), HEALTH_BAR_BACKGROUND_TEXTURE, barColor, false); + RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width * health, height, health, 1f, new Vec3d(width * -0.5f, 0, 0.003f), HEALTH_BAR_TEXTURE, barColor, false); } } } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java index 655e5e707b..29ff52a537 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java @@ -285,7 +285,20 @@ public static void renderQuad(WorldRenderContext context, Vec3d[] points, float[ RenderSystem.depthFunc(GL11.GL_LEQUAL); } - public static void renderTextureQuad(WorldRenderContext context, Vec3d pos, float width, float height, float textureWidth, float textureHeight, Vec3d renderOffset, Identifier texture, Color shaderColor, boolean throughWalls) { + /** + * Renders a texture in world space facing the player (like a name tag) + * @param context world render context + * @param pos world position + * @param width rendered width + * @param height rendered height + * @param textureWidth amount of texture rendered width + * @param textureHeight amount of texture rendered height + * @param renderOffset offset once it's been placed in the world facing the player + * @param texture reference to texture to render + * @param shaderColor color to apply to the texture + * @param throughWalls if it should render though walls + */ + public static void renderTextureInWorld(WorldRenderContext context, Vec3d pos, float width, float height, float textureWidth, float textureHeight, Vec3d renderOffset, Identifier texture, Color shaderColor, boolean throughWalls) { Matrix4f positionMatrix = new Matrix4f(); Camera camera = context.camera(); Vec3d cameraPos = camera.getPos(); From d2a82d446584cfdbf008df28810066eaf051aad9 Mon Sep 17 00:00:00 2001 From: olim Date: Thu, 17 Oct 2024 20:08:11 +0100 Subject: [PATCH 5/8] increase default size --- .../de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java index 5e6d8a9f83..80dbe45f50 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java @@ -290,7 +290,7 @@ public static class HealthBars { public boolean enabled = true; @SerialEntry - public float scale = 1f; + public float scale = 1.5f; @SerialEntry public boolean removeHealthFromName = true; From f6caa10b0f2695a9ef547645d9de18b5c5a4d5b7 Mon Sep 17 00:00:00 2001 From: olim Date: Mon, 21 Oct 2024 21:24:23 +0100 Subject: [PATCH 6/8] improve removing health code and incorrectly spell amour --- .../hysky/skyblocker/skyblock/HealthBars.java | 68 +++++++++++++------ 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java index 9929f9b43e..4a1820f5b0 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java @@ -17,6 +17,7 @@ import net.minecraft.text.Text; import net.minecraft.util.Identifier; import net.minecraft.util.math.Vec3d; +import org.apache.commons.lang3.StringUtils; import java.awt.*; import java.util.List; @@ -46,7 +47,7 @@ private static void reset() { } /** - * remove dead armour stands from health bars + * remove dead armor stands from health bars * * @param entity dying entity */ @@ -58,23 +59,23 @@ public static void onEntityDespawn(Entity entity, ClientWorld clientWorld) { } /** - * Processes armourtand updates and if it's a mob with health get the value of its health and save it the hashmap + * Processes armorstand updates and if it's a mob with health get the value of its health and save it the hashmap * - * @param armorStand updated armourstand + * @param armorStand updated armorstand */ public static void HeathBar(ArmorStandEntity armorStand) { if (!armorStand.isInvisible() || !armorStand.hasCustomName() || !armorStand.isCustomNameVisible() || !SkyblockerConfigManager.get().uiAndVisuals.healthBars.enabled) { return; } - //check if armour stand is dead and remove it from list + //check if armor stand is dead and remove it from list if (armorStand.isDead()) { healthValues.removeFloat(armorStand); mobStartingHealth.removeInt(armorStand); return; } - //check to see if the armour stand is a mob label with health + //check to see if the armor stand is a mob label with health if (armorStand.getCustomName() == null) { return; } @@ -91,7 +92,7 @@ public static void HeathBar(ArmorStandEntity armorStand) { float health = (float) firstValue / secondValue; healthValues.put(armorStand, health); - //edit armour stand name to remove health + //edit armor stand name to remove health boolean removeValue = SkyblockerConfigManager.get().uiAndVisuals.healthBars.removeHealthFromName; boolean removeMax = SkyblockerConfigManager.get().uiAndVisuals.healthBars.removeMaxHealthFromName; //if both disabled no need to edit name @@ -100,28 +101,52 @@ public static void HeathBar(ArmorStandEntity armorStand) { } MutableText cleanedText = Text.empty(); List parts = armorStand.getCustomName().getSiblings(); + //loop though name and add every part to a new text skipping over the hidden health values + int healthStartIndex = -1; + System.out.println(healthMatcher.group(0).toString()+"testing"); for (int i = 0; i < parts.size(); i++) { //remove value from name - if (removeValue && i < parts.size() - 3 && parts.get(i).getString().equals(healthMatcher.group(1)) && parts.get(i + 1).getString().equals("/") && parts.get(i + 2).getString().equals(healthMatcher.group(4)) && parts.get(i + 3).getString().equals("❤")) { - continue; + if (i < parts.size() - 4 && StringUtils.join(parts.subList(i+1, i + 5).stream().map(Text::getString).toArray(), "").equals(healthMatcher.group(0))) { + healthStartIndex = i; } - //remove slash from max and skip over the value - if (removeMax && i < parts.size() - 2 && parts.get(i).getString().equals("/") && parts.get(i + 1).getString().equals(healthMatcher.group(4)) && parts.get(i + 2).getString().equals("❤")) { - //remove the value as well - i += 1; - continue; - } - //if both enabled remove "❤" - if (removeValue && removeMax && parts.get(i).getString().equals("❤")) { - continue; + if (healthStartIndex != -1) { + //skip parts of the health offset form staring index + switch (i - healthStartIndex) { + case 0 -> { // space before health + if (removeMax && removeValue) { + continue; + } + } + case 1 -> { // current health value + if (removeValue) { + continue; + } + } + case 2 -> { // "/" separating health values + if (removeMax) { + continue; + } + } + case 3 -> { // max health value + if (removeMax) { + continue; + } + } + case 4 -> { // "❤" at end of health + if (removeMax && removeValue) { + continue; + } + } + } } + cleanedText.append(parts.get(i)); } armorStand.setCustomName(cleanedText); } /** - * Processes armour stands that only have a health value and no max health + * Processes armor stands that only have a health value and no max health * * @param armorStand armorstand to check the name of */ @@ -153,11 +178,12 @@ private static void HealthOnlyCheck(ArmorStandEntity armorStand) { } MutableText cleanedText = Text.empty(); List parts = armorStand.getCustomName().getSiblings(); + //loop though name and add every part to a new text skipping over the health value for (int i = 0; i < parts.size(); i++) { - //remove value from name and skip over heart - if (i < parts.size() - 1 && parts.get(i).getString().equals(healthOnlyMatcher.group(1)) && parts.get(i + 1).getString().equals("❤")) { + //skip space before value, value and heart from name + if (i < parts.size() - 2 && StringUtils.join(parts.subList(i+1, i + 3).stream().map(Text::getString).toArray(), "").equals(healthOnlyMatcher.group(0))) { //skip the heart - i += 1; + i += 2; continue; } cleanedText.append(parts.get(i)); From b111cde5e2f7463c0d0378ce96666765a2f3d29c Mon Sep 17 00:00:00 2001 From: olim Date: Wed, 30 Oct 2024 20:23:14 +0000 Subject: [PATCH 7/8] show when the nametag is shown let it be rendered though walls if the nametag is being rendered --- .../de/hysky/skyblocker/skyblock/HealthBars.java | 12 ++++++++---- .../hysky/skyblocker/utils/render/RenderHelper.java | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java index 4a1820f5b0..082b214a0b 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java @@ -204,6 +204,8 @@ private static void render(WorldRenderContext context) { boolean hideFullHealth = SkyblockerConfigManager.get().uiAndVisuals.healthBars.hideFullHealth; float scale = SkyblockerConfigManager.get().uiAndVisuals.healthBars.scale; float tickDelta = context.tickCounter().getTickDelta(false); + float width = scale; + float height = scale * 0.1f; for (Object2FloatMap.Entry healthValue : healthValues.object2FloatEntrySet()) { //if the health bar is full and the setting is enabled to hide it stop rendering it @@ -213,11 +215,13 @@ private static void render(WorldRenderContext context) { } ArmorStandEntity armorStand = healthValue.getKey(); + //only render health bar if name is visible + if (!armorStand.shouldRenderName()) { + return; + } // Render the health bar texture with scaling based on health percentage - float width = scale; - float height = scale * 0.1f; - RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width, height, 1f, 1f, new Vec3d(width * -0.5f, 0, 0), HEALTH_BAR_BACKGROUND_TEXTURE, barColor, false); - RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width * health, height, health, 1f, new Vec3d(width * -0.5f, 0, 0.003f), HEALTH_BAR_TEXTURE, barColor, false); + RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width, height, 1f, 1f, new Vec3d(width * -0.5f, 0, 0), HEALTH_BAR_BACKGROUND_TEXTURE, barColor, true); + RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width * health, height, health, 1f, new Vec3d(width * -0.5f, 0, 0.003f), HEALTH_BAR_TEXTURE, barColor, true); } } } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java index 29ff52a537..8e6cdd91fa 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java @@ -311,7 +311,7 @@ public static void renderTextureInWorld(WorldRenderContext context, Vec3d pos, f RenderSystem.setShaderTexture(0, texture); RenderSystem.setShaderColor(shaderColor.getRed() / 255f, shaderColor.getGreen() / 255f, shaderColor.getBlue() / 255f, shaderColor.getAlpha() / 255f); - RenderSystem.setShader(GameRenderer::getPositionTexProgram); + RenderSystem.setShader(ShaderProgramKeys.POSITION_TEX); RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); RenderSystem.disableCull(); From 08873e02860ef4b6bdd2234293d7ed602e7d9c73 Mon Sep 17 00:00:00 2001 From: olim Date: Sun, 3 Nov 2024 20:49:37 +0000 Subject: [PATCH 8/8] add suggested changes add fading between colours and fix problems --- .../categories/UIAndVisualsCategory.java | 26 +++++++-- .../config/configs/UIAndVisualsConfig.java | 9 ++- .../mixins/ClientPlayNetworkHandlerMixin.java | 2 +- .../hysky/skyblocker/skyblock/HealthBars.java | 57 ++++++++++++++++--- .../assets/skyblocker/lang/en_us.json | 8 ++- 5 files changed, 86 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java index 6326180569..8d26e25110 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java @@ -443,6 +443,7 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .collapsed(true) .option(Option.createBuilder() .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.enabled")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.enabled.@Tooltip"))) .binding(defaults.uiAndVisuals.healthBars.enabled, () -> config.uiAndVisuals.healthBars.enabled, newValue -> config.uiAndVisuals.healthBars.enabled = newValue) @@ -488,10 +489,27 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .controller(ConfigUtils::createBooleanController) .build()) .option(Option.createBuilder() - .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.barColor")) - .binding(defaults.uiAndVisuals.healthBars.barColor, - () -> config.uiAndVisuals.healthBars.barColor, - newValue -> config.uiAndVisuals.healthBars.barColor = newValue) + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.fullBarColor")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.fullBarColor.@Tooltip"))) + .binding(defaults.uiAndVisuals.healthBars.fullBarColor, + () -> config.uiAndVisuals.healthBars.fullBarColor, + newValue -> config.uiAndVisuals.healthBars.fullBarColor = newValue) + .controller(ColorControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.halfBarColor")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.halfBarColor.@Tooltip"))) + .binding(defaults.uiAndVisuals.healthBars.halfBarColor, + () -> config.uiAndVisuals.healthBars.halfBarColor, + newValue -> config.uiAndVisuals.healthBars.halfBarColor = newValue) + .controller(ColorControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.emptyBarColor")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.healthBars.emptyBarColor.@Tooltip"))) + .binding(defaults.uiAndVisuals.healthBars.emptyBarColor, + () -> config.uiAndVisuals.healthBars.emptyBarColor, + newValue -> config.uiAndVisuals.healthBars.emptyBarColor = newValue) .controller(ColorControllerBuilder::create) .build()) .build() diff --git a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java index 80dbe45f50..fcc5355de8 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java @@ -304,7 +304,14 @@ public static class HealthBars { @SerialEntry public boolean hideFullHealth = false; + + @SerialEntry + public Color fullBarColor = new Color(0x00FF00); + + @SerialEntry + public Color halfBarColor = new Color(0xFF4600); + @SerialEntry - public Color barColor = new Color(0xFFFFFF); + public Color emptyBarColor = new Color(0xFF0000); } } diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index c2b95200b5..9be0bfda7b 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -127,7 +127,7 @@ public abstract class ClientPlayNetworkHandlerMixin { EggFinder.checkIfEgg(armorStandEntity); try { //Prevent packet handling fails if something goes wrong so that entity trackers still update, just without compact damage numbers CompactDamage.compactDamage(armorStandEntity); - HealthBars.HeathBar(armorStandEntity); + HealthBars.heathBar(armorStandEntity); } catch (Exception e) { LOGGER.error("[Skyblocker Compact Damage] Failed to compact damage number", e); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java index 082b214a0b..36ec3ba1e6 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java @@ -63,7 +63,7 @@ public static void onEntityDespawn(Entity entity, ClientWorld clientWorld) { * * @param armorStand updated armorstand */ - public static void HeathBar(ArmorStandEntity armorStand) { + public static void heathBar(ArmorStandEntity armorStand) { if (!armorStand.isInvisible() || !armorStand.hasCustomName() || !armorStand.isCustomNameVisible() || !SkyblockerConfigManager.get().uiAndVisuals.healthBars.enabled) { return; } @@ -82,7 +82,7 @@ public static void HeathBar(ArmorStandEntity armorStand) { Matcher healthMatcher = HEALTH_PATTERN.matcher(armorStand.getCustomName().getString()); //if a health ratio can not be found send onto health only pattern if (!healthMatcher.find()) { - HealthOnlyCheck(armorStand); + healthOnlyCheck(armorStand); return; } @@ -103,10 +103,9 @@ public static void HeathBar(ArmorStandEntity armorStand) { List parts = armorStand.getCustomName().getSiblings(); //loop though name and add every part to a new text skipping over the hidden health values int healthStartIndex = -1; - System.out.println(healthMatcher.group(0).toString()+"testing"); for (int i = 0; i < parts.size(); i++) { //remove value from name - if (i < parts.size() - 4 && StringUtils.join(parts.subList(i+1, i + 5).stream().map(Text::getString).toArray(), "").equals(healthMatcher.group(0))) { + if (i < parts.size() - 4 && StringUtils.join(parts.subList(i + 1, i + 5).stream().map(Text::getString).toArray(), "").equals(healthMatcher.group(0))) { healthStartIndex = i; } if (healthStartIndex != -1) { @@ -150,7 +149,7 @@ public static void HeathBar(ArmorStandEntity armorStand) { * * @param armorStand armorstand to check the name of */ - private static void HealthOnlyCheck(ArmorStandEntity armorStand) { + private static void healthOnlyCheck(ArmorStandEntity armorStand) { if (!SkyblockerConfigManager.get().uiAndVisuals.healthBars.applyToHealthOnlyMobs || armorStand.getCustomName() == null) { return; } @@ -181,7 +180,7 @@ private static void HealthOnlyCheck(ArmorStandEntity armorStand) { //loop though name and add every part to a new text skipping over the health value for (int i = 0; i < parts.size(); i++) { //skip space before value, value and heart from name - if (i < parts.size() - 2 && StringUtils.join(parts.subList(i+1, i + 3).stream().map(Text::getString).toArray(), "").equals(healthOnlyMatcher.group(0))) { + if (i < parts.size() - 2 && StringUtils.join(parts.subList(i + 1, i + 3).stream().map(Text::getString).toArray(), "").equals(healthOnlyMatcher.group(0))) { //skip the heart i += 2; continue; @@ -200,7 +199,9 @@ private static void render(WorldRenderContext context) { if (!SkyblockerConfigManager.get().uiAndVisuals.healthBars.enabled || healthValues.isEmpty()) { return; } - Color barColor = SkyblockerConfigManager.get().uiAndVisuals.healthBars.barColor; + Color fullColor = SkyblockerConfigManager.get().uiAndVisuals.healthBars.fullBarColor; + Color halfColor = SkyblockerConfigManager.get().uiAndVisuals.healthBars.halfBarColor; + Color emptyColor = SkyblockerConfigManager.get().uiAndVisuals.healthBars.emptyBarColor; boolean hideFullHealth = SkyblockerConfigManager.get().uiAndVisuals.healthBars.hideFullHealth; float scale = SkyblockerConfigManager.get().uiAndVisuals.healthBars.scale; float tickDelta = context.tickCounter().getTickDelta(false); @@ -219,9 +220,47 @@ private static void render(WorldRenderContext context) { if (!armorStand.shouldRenderName()) { return; } + //gets the mixed color of the health bar + Color mixedColor = fadeBetweenThreeColors(emptyColor, halfColor, fullColor, health); // Render the health bar texture with scaling based on health percentage - RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width, height, 1f, 1f, new Vec3d(width * -0.5f, 0, 0), HEALTH_BAR_BACKGROUND_TEXTURE, barColor, true); - RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width * health, height, health, 1f, new Vec3d(width * -0.5f, 0, 0.003f), HEALTH_BAR_TEXTURE, barColor, true); + RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width, height, 1f, 1f, new Vec3d(width * -0.5f, 0, 0), HEALTH_BAR_BACKGROUND_TEXTURE, mixedColor, true); + RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width * health, height, health, 1f, new Vec3d(width * -0.5f, 0, 0.003f), HEALTH_BAR_TEXTURE, mixedColor, true); } } + + + /** + * Interoperates between 3 colors + * + * @param color1 full color + * @param color2 half color + * @param color3 empty color + * @param t position between the colors from 0-1 + * @return the interpolated color + */ + public static Color fadeBetweenThreeColors(Color color1, Color color2, Color color3, double t) { + Color startColor; + Color endColor; + double subT; + + if (t <= 0.5) { + // First half: color1 to color2 + startColor = color1; + endColor = color2; + subT = t / 0.5; + } else { + // Second half: color2 to color3 + startColor = color2; + endColor = color3; + subT = (t - 0.5) / 0.5; + } + + // Interpolate each RGB component + int red = (int) (startColor.getRed() + (endColor.getRed() - startColor.getRed()) * subT); + int green = (int) (startColor.getGreen() + (endColor.getGreen() - startColor.getGreen()) * subT); + int blue = (int) (startColor.getBlue() + (endColor.getBlue() - startColor.getBlue()) * subT); + + return new Color(red, green, blue); + } + } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 9315dd6436..b93e3205cc 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -754,6 +754,7 @@ "skyblocker.config.uiAndVisuals.healthBars": "Custom Mob Health Bars", "skyblocker.config.uiAndVisuals.healthBars.enabled": "Enabled", + "skyblocker.config.uiAndVisuals.healthBars.enabled.@Tooltip": "(only updates when health updates)", "skyblocker.config.uiAndVisuals.healthBars.scale": "Scale", "skyblocker.config.uiAndVisuals.healthBars.removeHealthFromName": "Remove Health From Name", "skyblocker.config.uiAndVisuals.healthBars.removeHealthFromName.@Tooltip": "Remove the current health of mobs from their nametag", @@ -763,7 +764,12 @@ "skyblocker.config.uiAndVisuals.healthBars.applyToHealthOnlyMobs.@Tooltip": "Also add health bars to mobs with only a current health value (will sometimes be wrong if mob is damaged before loading in)", "skyblocker.config.uiAndVisuals.healthBars.hideFullHealth": "Hide Full Health Bars", "skyblocker.config.uiAndVisuals.healthBars.hideFullHealth.@Tooltip": "Don't show the health bar when its full", - "skyblocker.config.uiAndVisuals.healthBars.barColor": "Health Bar Color", + "skyblocker.config.uiAndVisuals.healthBars.fullBarColor": "Full Health bar Color", + "skyblocker.config.uiAndVisuals.healthBars.fullBarColor.@Tooltip": "Color of health bar when mob is at full hp", + "skyblocker.config.uiAndVisuals.healthBars.halfBarColor": "Half Health Bar Color", + "skyblocker.config.uiAndVisuals.healthBars.halfBarColor.@Tooltip": "Color of health bar when mob is at half hp", + "skyblocker.config.uiAndVisuals.healthBars.emptyBarColor": "Empty Health Bar Color", + "skyblocker.config.uiAndVisuals.healthBars.emptyBarColor.@Tooltip": "Color of health bar when mob is at full hp", "skyblocker.config.uiAndVisuals.inputCalculator": "Input Calculator", "skyblocker.config.uiAndVisuals.inputCalculator.enabled": "Enable Sign Calculator",