From c03ab7ebefafc864bf3c24ea1f6569c67b75b062 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Sat, 7 Mar 2026 00:25:18 +0100 Subject: [PATCH] nametags fullbright and health display with keystrokes --- .../interfaces/HealthDisplayInterface.java | 7 + .../interfaces/KeystrokesInterface.java | 13 ++ .../modules/interfaces/NametagsInterface.java | 23 +++ .../saturnclient/modules/mods/Fullbright.java | 39 +++++ .../modules/mods/HealthDisplay.java | 97 +++++++++++ .../saturnclient/modules/mods/Keystrokes.java | 156 ++++++++++++++++++ .../saturnclient/modules/mods/Nametags.java | 125 ++++++++++++++ 7 files changed, 460 insertions(+) create mode 100644 src/main/java/org/saturnclient/modules/interfaces/HealthDisplayInterface.java create mode 100644 src/main/java/org/saturnclient/modules/interfaces/KeystrokesInterface.java create mode 100644 src/main/java/org/saturnclient/modules/interfaces/NametagsInterface.java create mode 100644 src/main/java/org/saturnclient/modules/mods/Fullbright.java create mode 100644 src/main/java/org/saturnclient/modules/mods/HealthDisplay.java create mode 100644 src/main/java/org/saturnclient/modules/mods/Keystrokes.java create mode 100644 src/main/java/org/saturnclient/modules/mods/Nametags.java diff --git a/src/main/java/org/saturnclient/modules/interfaces/HealthDisplayInterface.java b/src/main/java/org/saturnclient/modules/interfaces/HealthDisplayInterface.java new file mode 100644 index 0000000..cb8d20f --- /dev/null +++ b/src/main/java/org/saturnclient/modules/interfaces/HealthDisplayInterface.java @@ -0,0 +1,7 @@ +package org.saturnclient.modules.interfaces; + +public interface HealthDisplayInterface { + + float getPlayerHealth(); + +} \ No newline at end of file diff --git a/src/main/java/org/saturnclient/modules/interfaces/KeystrokesInterface.java b/src/main/java/org/saturnclient/modules/interfaces/KeystrokesInterface.java new file mode 100644 index 0000000..5c220ef --- /dev/null +++ b/src/main/java/org/saturnclient/modules/interfaces/KeystrokesInterface.java @@ -0,0 +1,13 @@ +package org.saturnclient.modules.interfaces; + +public interface KeystrokesInterface { + + boolean isForwardPressed(); + boolean isBackPressed(); + boolean isLeftPressed(); + boolean isRightPressed(); + boolean isJumpPressed(); + boolean isAttackPressed(); + boolean isUsePressed(); + +} \ No newline at end of file diff --git a/src/main/java/org/saturnclient/modules/interfaces/NametagsInterface.java b/src/main/java/org/saturnclient/modules/interfaces/NametagsInterface.java new file mode 100644 index 0000000..5834508 --- /dev/null +++ b/src/main/java/org/saturnclient/modules/interfaces/NametagsInterface.java @@ -0,0 +1,23 @@ +package org.saturnclient.modules.interfaces; + +public interface NametagsInterface { + + public static interface EntityState { + + String getCustomName(); + + float getHealth(); + + float getMaxHealth(); + + EntityType getEntityType(); + } + + public static enum EntityType { + PLAYER, + PASSIVE, + HOSTILE, + OTHER + } + +} \ No newline at end of file diff --git a/src/main/java/org/saturnclient/modules/mods/Fullbright.java b/src/main/java/org/saturnclient/modules/mods/Fullbright.java new file mode 100644 index 0000000..e067c37 --- /dev/null +++ b/src/main/java/org/saturnclient/modules/mods/Fullbright.java @@ -0,0 +1,39 @@ +package org.saturnclient.modules.mods; + +import org.saturnclient.modules.Module; +import org.saturnclient.modules.ModuleDetails; +import org.saturnclient.config.manager.Property; + +public class Fullbright extends Module { + private static Property enabled = Property.bool(false); + private static Property brightness = Property.integer(100); + + public Fullbright() { + super( + new ModuleDetails("Fullbright", "fullbright") + .description("Allows you to see in the dark") + .tags("Camera") + .version("v0.1.0"), + enabled.named("Enabled"), + brightness.named("Brightness %")); + } + + @Override + public boolean isEnabled() { + return enabled.value; + } + + @Override + public void onEnabled(boolean e) { + enabled.value = e; + } + + public static boolean shouldOverrideBrightness() { + return enabled.value; + } + + public static float getBrightnessValue() { + return brightness.value / 10; + } + +} diff --git a/src/main/java/org/saturnclient/modules/mods/HealthDisplay.java b/src/main/java/org/saturnclient/modules/mods/HealthDisplay.java new file mode 100644 index 0000000..b217101 --- /dev/null +++ b/src/main/java/org/saturnclient/modules/mods/HealthDisplay.java @@ -0,0 +1,97 @@ +package org.saturnclient.modules.mods; + +import org.saturnclient.modules.HudMod; +import org.saturnclient.modules.ModDimensions; +import org.saturnclient.modules.Module; +import org.saturnclient.modules.ModuleDetails; +import org.saturnclient.modules.interfaces.HealthDisplayInterface; +import org.saturnclient.config.manager.Property; +import org.saturnclient.ui.RenderScope; +import org.saturnclient.ui.resources.Fonts; + +public class HealthDisplay extends Module implements HudMod { + + private static Property enabled = Property.bool(false); + private static Property displayMode = Property.select(0, "Health", "Hearts"); + private static Property decimals = Property.select(0, "0", "1", "2"); + + private static ModDimensions dimensions = new ModDimensions(40, 18); + + private final HealthDisplayInterface minecraft; + + public HealthDisplay(HealthDisplayInterface minecraft) { + super(new ModuleDetails("Health display", "health") + .description("Displays your current health") + .version("v0.1.0") + .tags("Utility"), + enabled.named("Enabled"), + displayMode.named("Display mode"), + decimals.named("Decimals"), + dimensions.prop()); + + this.minecraft = minecraft; + } + + @Override + public void renderDummy(RenderScope scope) { + renderHealth(10, scope); + } + + @Override + public void renderHud(RenderScope scope) { + float health = minecraft.getPlayerHealth(); + + switch (displayMode.value) { + case 1: + health = health / 2; + break; + } + + renderHealth(health, scope); + } + + public void renderHealth(float health, RenderScope scope) { + + String text = ""; + + switch (decimals.value) { + case 0: + text = String.format("%.0f ", health); + break; + + case 1: + text = String.format("%.1f ", health); + break; + + case 2: + text = String.format("%.2f ", health); + break; + } + + scope.drawText( + text, + 0, + 0, + dimensions.font.value, + dimensions.fgColor.value + ); + + dimensions.width = Fonts.getWidth(text, dimensions.font.value); + dimensions.height = 18 * text.split("\n").length; + } + + @Override + public boolean isEnabled() { + return enabled.value; + } + + @Override + public ModDimensions getDimensions() { + return dimensions; + } + + @Override + public void onEnabled(boolean e) { + enabled.value = e; + } +} \ No newline at end of file diff --git a/src/main/java/org/saturnclient/modules/mods/Keystrokes.java b/src/main/java/org/saturnclient/modules/mods/Keystrokes.java new file mode 100644 index 0000000..acf7256 --- /dev/null +++ b/src/main/java/org/saturnclient/modules/mods/Keystrokes.java @@ -0,0 +1,156 @@ +package org.saturnclient.modules.mods; + +import org.saturnclient.modules.Module; +import org.saturnclient.modules.HudMod; +import org.saturnclient.modules.ModDimensions; +import org.saturnclient.modules.ModuleDetails; +import org.saturnclient.modules.interfaces.KeystrokesInterface; +import org.saturnclient.config.manager.Property; +import org.saturnclient.ui.RenderScope; + +public class Keystrokes extends Module implements HudMod { + + private static final Property enabled = Property.bool(false); + private static final Property showMouse = Property.bool(true); + private static final Property showSpace = Property.bool(false); + + private static final Property clickBg = Property.color(0xFFCCCCCC); + private static final Property clickFg = Property.color(0xFFFFFFFF); + + private static final ModDimensions dimensions = new ModDimensions(78, 54); + + private final KeystrokesInterface minecraft; + + // Key states + private boolean w, a, s, d, lmb, rmb, space; + + // Layout constants + private static final int KEY_SIZE = 24; + private static final int KEY_SPACING = 3; + private static final int MOUSE_WIDTH = 38; + private static final int MOUSE_HEIGHT = 24; + private static final int SPACE_HEIGHT = 19; + + public Keystrokes(KeystrokesInterface minecraft) { + super(new ModuleDetails("Keystrokes", "keystrokes") + .description("Displays movement keystrokes") + .version("v0.2.0") + .tags("Utility"), + + enabled.named("Enabled"), + showMouse.named("Show mouse clicks"), + showSpace.named("Show space"), + dimensions.prop(), + clickFg.named("Clicked fg"), + clickBg.named("Clicked bg")); + + this.minecraft = minecraft; + + dimensions.renderBackground = false; + } + + @Override + public void tick() { + w = minecraft.isForwardPressed(); + a = minecraft.isLeftPressed(); + s = minecraft.isBackPressed(); + d = minecraft.isRightPressed(); + lmb = minecraft.isAttackPressed(); + rmb = minecraft.isUsePressed(); + space = minecraft.isJumpPressed(); + + updateHeight(); + } + + private void updateHeight() { + int height = KEY_SIZE * 2 + KEY_SPACING; + + if (showSpace.value) + height += SPACE_HEIGHT + KEY_SPACING; + + if (showMouse.value) + height += MOUSE_HEIGHT + KEY_SPACING; + + dimensions.height = height; + } + + @Override + public void renderDummy(RenderScope scope) { + render(scope, false); + } + + @Override + public void renderHud(RenderScope scope) { + render(scope, true); + } + + private void render(RenderScope scope, boolean live) { + + int yOffset = 0; + + // --- W --- + renderKey(scope, live && w, 'W', KEY_SIZE + KEY_SPACING, yOffset); + + // --- A S D --- + yOffset += KEY_SIZE + KEY_SPACING; + + renderKey(scope, live && a, 'A', 0, yOffset); + renderKey(scope, live && s, 'S', KEY_SIZE + KEY_SPACING, yOffset); + renderKey(scope, live && d, 'D', (KEY_SIZE + KEY_SPACING) * 2, yOffset); + + yOffset += KEY_SIZE + KEY_SPACING; + + // --- SPACE --- + if (showSpace.value) { + renderKeySpace(scope, live && space, 0, yOffset); + yOffset += SPACE_HEIGHT + KEY_SPACING; + } + + // --- MOUSE --- + if (showMouse.value) { + renderKeyMouse(scope, live && lmb, "LMB", 0, yOffset); + renderKeyMouse(scope, live && rmb, "RMB", MOUSE_WIDTH + KEY_SPACING, yOffset); + } + } + + private void renderKey(RenderScope scope, boolean pressed, char c, int x, int y) { + int bg = pressed ? clickBg.value : dimensions.bgColor.value; + int fg = pressed ? clickFg.value : dimensions.fgColor.value; + + scope.drawRoundedRectangle(x, y, KEY_SIZE, KEY_SIZE, dimensions.radius.value, bg); + scope.drawText(0.6f, String.valueOf(c), x + 9, y + 7, dimensions.font.value, fg); + } + + private void renderKeyMouse(RenderScope scope, boolean pressed, String text, int x, int y) { + int bg = pressed ? clickBg.value : dimensions.bgColor.value; + int fg = pressed ? clickFg.value : dimensions.fgColor.value; + + scope.drawRoundedRectangle(x, y, MOUSE_WIDTH, MOUSE_HEIGHT, dimensions.radius.value, bg); + scope.drawText(0.6f, text, x + 9, y + 7, dimensions.font.value, fg); + } + + private void renderKeySpace(RenderScope scope, boolean pressed, int x, int y) { + int bg = pressed ? clickBg.value : dimensions.bgColor.value; + int fg = pressed ? clickFg.value : dimensions.fgColor.value; + + scope.drawRoundedRectangle(x, y, dimensions.width, SPACE_HEIGHT, dimensions.radius.value, bg); + + int lineY = y + SPACE_HEIGHT / 2; + scope.drawRect(x + (dimensions.width - 30) / 2, lineY, 30, 1, fg); + } + + @Override + public boolean isEnabled() { + return enabled.value; + } + + @Override + public ModDimensions getDimensions() { + return dimensions; + } + + @Override + public void onEnabled(boolean e) { + enabled.value = e; + } +} \ No newline at end of file diff --git a/src/main/java/org/saturnclient/modules/mods/Nametags.java b/src/main/java/org/saturnclient/modules/mods/Nametags.java new file mode 100644 index 0000000..41071f4 --- /dev/null +++ b/src/main/java/org/saturnclient/modules/mods/Nametags.java @@ -0,0 +1,125 @@ +package org.saturnclient.modules.mods; + +import org.saturnclient.modules.Module; +import org.saturnclient.modules.ModuleDetails; +import org.saturnclient.config.manager.Property; +import org.saturnclient.modules.interfaces.NametagsInterface; + +public class Nametags extends Module { + + private static Property enabled = Property.bool(false); + private static Property healthDisplay = Property.bool(false); + private static Property players = Property.bool(true); + private static Property hostile = Property.bool(false); + private static Property passive = Property.bool(false); + private static Property heartEmoji = Property.bool(true); + private static Property obfuscate = Property.bool(false); + private static Property unit = Property.select(0, "Health", "Hearts"); + private static Property format = Property.select(0, "Value", "Value / Total", "%"); + private static Property nameColor = Property.select(15, + "Black","Dark Blue","Dark Green","Dark Aqua","Dark Red","Dark Purple","Gold","Gray", + "Dark Gray","Blue","Green","Aqua","Red","Light Purple","Yellow","White"); + private static Property healthColor = Property.select(12, + "Black","Dark Blue","Dark Green","Dark Aqua","Dark Red","Dark Purple","Gold","Gray", + "Dark Gray","Blue","Green","Aqua","Red","Light Purple","Yellow","White"); + + private static final String[] COLOR_CODES = { + "§0", "§1", "§2", "§3", "§4", "§5", "§6", "§7", + "§8", "§9", "§a", "§b", "§c", "§d", "§e", "§f" + }; + + private final NametagsInterface minecraft; + + public Nametags(NametagsInterface minecraft) { + super(new ModuleDetails("Nametags", "nametags") + .description("Allows you to modify entity nametags") + .tags("Camera", "Utility") + .version("0.1.0"), + enabled.named("Enabled"), + healthDisplay.named("Display health"), + players.named("Players"), + hostile.named("Hostile"), + passive.named("Passive"), + heartEmoji.named("Heart emoji"), + obfuscate.named("Obfuscate enemy names"), + unit.named("Unit"), + format.named("Format"), + nameColor.named("Name color"), + healthColor.named("Health color")); + + this.minecraft = minecraft; + } + + @Override + public void tick() { + // Currently unused + } + + public static String getHealthString(NametagsInterface.EntityState state, float health, float maxHealth) { + String ret = ""; + + switch (format.value) { + case 0: // value + ret = String.format("%.1f", health / (unit.value + 1)); + break; + + case 1: // value / total + ret = String.format("%.1f", health / (unit.value + 1)) + " / " + + String.format("%.1f", maxHealth / (unit.value + 1)); + break; + + case 2: // % + ret = String.format("%.0f%%", (health / maxHealth) * 100); + break; + } + + return ret; + } + + public static String getNametagString(NametagsInterface.EntityState state) { + if (state.getCustomName() == null) + return null; + + if (!healthDisplay.value) + return state.getCustomName(); // name only + + float health = state.getHealth(); + float maxHealth = state.getMaxHealth(); + NametagsInterface.EntityType type = state.getEntityType(); + + if (type == NametagsInterface.EntityType.PASSIVE && !passive.value) + return null; + if (type == NametagsInterface.EntityType.HOSTILE && !hostile.value) + return null; + if (type == NametagsInterface.EntityType.PLAYER && !players.value) + return null; + if (type == NametagsInterface.EntityType.OTHER) + return null; + + if (health < 0f || maxHealth < 0f || health > maxHealth) + return null; + + String hpColor = COLOR_CODES[healthColor.value]; + String color = COLOR_CODES[nameColor.value]; + String heart = heartEmoji.value ? " ❤" : ""; + String obf = obfuscate.value ? "§k" : ""; + String reset = "§r"; + + return color + obf + state.getCustomName() + reset + " " + hpColor + + getHealthString(state, health, maxHealth) + heart; + } + + public static boolean shouldReplaceName() { + return enabled.value; + } + + @Override + public void onEnabled(boolean e) { + enabled.value = e; + } + + @Override + public boolean isEnabled() { + return enabled.value; + } +} \ No newline at end of file