diff --git a/src/main/java/net/wurstclient/hacks/HealthTagsHack.java b/src/main/java/net/wurstclient/hacks/HealthTagsHack.java index 94822fba7e..e299fdbe87 100644 --- a/src/main/java/net/wurstclient/hacks/HealthTagsHack.java +++ b/src/main/java/net/wurstclient/hacks/HealthTagsHack.java @@ -7,44 +7,108 @@ */ package net.wurstclient.hacks; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.MobEntity; import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.wurstclient.Category; import net.wurstclient.SearchTags; +import net.wurstclient.events.RenderListener; import net.wurstclient.hack.Hack; +import net.wurstclient.settings.CheckboxSetting; +import net.wurstclient.settings.RoundingPrecisionSetting; +import net.wurstclient.util.RenderUtils; @SearchTags({"health tags"}) -public final class HealthTagsHack extends Hack +public final class HealthTagsHack extends Hack implements RenderListener { + private final CheckboxSetting mobs = new CheckboxSetting("Mobs", + "Displays health tags above mobs also.", false); + + private final CheckboxSetting showMaxHealth = + new CheckboxSetting("Show max health", "Also displays the entity's" + + " maximum health in addition to its current health.", false); + + private final RoundingPrecisionSetting precision = + new RoundingPrecisionSetting("Precision", + "Rounds the health value to the given number of decimal places.", 0, + 0, 3); + public HealthTagsHack() { super("HealthTags"); setCategory(Category.RENDER); + addSetting(mobs); + addSetting(showMaxHealth); + addSetting(precision); + } + + @Override + protected void onEnable() + { + EVENTS.add(RenderListener.class, this); + } + + @Override + protected void onDisable() + { + EVENTS.remove(RenderListener.class, this); } - public Text addHealth(LivingEntity entity, Text nametag) + @Override + public void onRender(MatrixStack matrixStack, float partialTicks) + { + if(!mobs.isChecked()) + return; + + VertexConsumerProvider.Immediate immediate = + MC.getBufferBuilders().getEntityVertexConsumers(); + + for(Entity e : MC.world.getEntities()) + { + if(!(e instanceof MobEntity entity)) + continue; + + Text text = addHealth(entity, Text.literal("")); + RenderUtils.renderTag(matrixStack, text, entity, immediate, + 0xffffff, !entity.hasCustomName() ? 0.5 : 1, partialTicks); + } + + immediate.draw(); + } + + public Text addHealth(LivingEntity entity, MutableText nametag) { if(!isEnabled()) return nametag; - int health = (int)entity.getHealth(); + float health = entity.getHealth(); + float maxHealth = entity.getMaxHealth(); + Formatting color = getColor(health, maxHealth); + + String healthString = precision.format(health); + if(showMaxHealth.isChecked()) + healthString += "/" + precision.format(maxHealth); + + if(!nametag.getString().isEmpty()) + nametag = nametag.append(Text.literal(" ")); - MutableText formattedHealth = Text.literal(" ") - .append(Integer.toString(health)).formatted(getColor(health)); - return ((MutableText)nametag).append(formattedHealth); + return nametag.append(Text.literal(healthString).formatted(color)); } - private Formatting getColor(int health) + private Formatting getColor(float health, float maxHealth) { - if(health <= 5) + if(health <= maxHealth * 0.25) return Formatting.DARK_RED; - if(health <= 10) + if(health <= maxHealth * 0.5) return Formatting.GOLD; - if(health <= 15) + if(health <= maxHealth * 0.75) return Formatting.YELLOW; return Formatting.GREEN; diff --git a/src/main/java/net/wurstclient/mixin/EntityRendererMixin.java b/src/main/java/net/wurstclient/mixin/EntityRendererMixin.java index a87533d699..da79da9231 100644 --- a/src/main/java/net/wurstclient/mixin/EntityRendererMixin.java +++ b/src/main/java/net/wurstclient/mixin/EntityRendererMixin.java @@ -24,6 +24,7 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityAttachmentType; import net.minecraft.entity.LivingEntity; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.math.Vec3d; import net.wurstclient.WurstClient; @@ -46,7 +47,7 @@ private void onRenderLabelIfPresent(T entity, Text text, // add HealthTags info if(entity instanceof LivingEntity) text = WurstClient.INSTANCE.getHax().healthTagsHack - .addHealth((LivingEntity)entity, text); + .addHealth((LivingEntity)entity, (MutableText)text); // do NameTags adjustments wurstRenderLabelIfPresent(entity, text, matrixStack, diff --git a/src/main/java/net/wurstclient/util/RenderUtils.java b/src/main/java/net/wurstclient/util/RenderUtils.java index 4b95e0ac5e..e032d2cd72 100644 --- a/src/main/java/net/wurstclient/util/RenderUtils.java +++ b/src/main/java/net/wurstclient/util/RenderUtils.java @@ -8,6 +8,7 @@ package net.wurstclient.util; import org.joml.Matrix4f; +import org.joml.Quaternionf; import org.joml.Vector3f; import org.lwjgl.opengl.GL11; @@ -15,17 +16,22 @@ import net.minecraft.block.Blocks; import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.font.TextRenderer.TextLayerType; import net.minecraft.client.gl.VertexBuffer; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.render.*; +import net.minecraft.client.render.entity.EntityRenderDispatcher; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.chunk.Chunk; import net.wurstclient.WurstClient; +import net.wurstclient.hacks.NameTagsHack; public enum RenderUtils { @@ -757,4 +763,52 @@ public static void drawItem(DrawContext context, ItemStack stack, int x, RenderSystem.setShaderColor(1, 1, 1, 1); } + + public static void renderTag(MatrixStack matrixStack, Text text, + Entity entity, VertexConsumerProvider provider, int color, + double vOffset, float partialTicks) + { + NameTagsHack nameTags = WurstClient.INSTANCE.getHax().nameTagsHack; + + EntityRenderDispatcher dispatcher = + WurstClient.MC.getEntityRenderDispatcher(); + double dist = dispatcher.getSquaredDistanceToCamera(entity); + if(dist > 4096 && !nameTags.isUnlimitedRange()) + return; + + matrixStack.push(); + + Vec3d camPos = RenderUtils.getCameraPos(); + Vec3d tagPos = EntityUtils.getLerpedPos(entity, partialTicks) + .subtract(camPos).add(0, entity.getHeight() + vOffset, 0); + matrixStack.translate(tagPos.x, tagPos.y, tagPos.z); + + matrixStack.multiply(dispatcher.getRotation().rotateY((float)Math.PI, + new Quaternionf())); + + float scale = 0.025F; + if(nameTags.isEnabled()) + { + double distance = WurstClient.MC.player.distanceTo(entity); + if(distance > 10) + scale *= distance / 10; + } + matrixStack.scale(-scale, -scale, scale); + + float bgOpacity = + WurstClient.MC.options.getTextBackgroundOpacity(0.25f); + int bgColor = (int)(bgOpacity * 255F) << 24; + + Matrix4f matrix = matrixStack.peek().getPositionMatrix(); + TextRenderer tr = WurstClient.MC.textRenderer; + int labelX = -tr.getWidth(text) / 2; + + tr.draw(text, labelX, 0, color, false, matrix, provider, + TextLayerType.NORMAL, bgColor, 15728880); + + tr.draw(text, labelX, 0, -1, false, matrix, provider, + TextLayerType.SEE_THROUGH, 0, 15728880); + + matrixStack.pop(); + } }