Skip to content

Commit 94f0306

Browse files
committed
add stats hud
1 parent d9d434c commit 94f0306

3 files changed

Lines changed: 176 additions & 1 deletion

File tree

src/main/java/com/nerds/addon/NerdAddon.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.mojang.logging.LogUtils;
44

55
import com.nerds.addon.commands.Coords;
6+
import com.nerds.addon.hud.StatsHud;
67
import com.nerds.addon.modules.*;
78
import meteordevelopment.meteorclient.addons.GithubRepo;
89
import meteordevelopment.meteorclient.addons.MeteorAddon;
@@ -33,6 +34,7 @@ public void onInitialize() {
3334
Commands.add(new Coords());
3435

3536
// HUD
37+
Hud.get().register(StatsHud.INFO);
3638
}
3739

3840
@Override

src/main/java/com/nerds/addon/commands/Coords.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void build(LiteralArgumentBuilder<CommandSource> builder) {
6666
}
6767

6868
private void copyCoords(String name, double x, double y, double z) {
69-
String text = "x: " + (int) Math.floor(x) + " y: " + (int) Math.floor(y) + " z: " + (int) Math.floor(z);
69+
String text = (int) Math.floor(x) + " " + (int) Math.floor(y) + " " + (int) Math.floor(z);
7070
mc.keyboard.setClipboard(text);
7171
boolean isSelf = mc.player != null && name.equals(mc.player.getGameProfile().name());
7272
info("successfully copied " + (isSelf ? "your" : name + "'s") + " coordinates: \n" + text);
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package com.nerds.addon.hud;
2+
3+
import com.nerds.addon.NerdAddon;
4+
import meteordevelopment.meteorclient.settings.BoolSetting;
5+
import meteordevelopment.meteorclient.settings.ColorSetting;
6+
import meteordevelopment.meteorclient.settings.EnumSetting;
7+
import meteordevelopment.meteorclient.settings.Setting;
8+
import meteordevelopment.meteorclient.settings.SettingGroup;
9+
import meteordevelopment.meteorclient.settings.StringSetting;
10+
import meteordevelopment.meteorclient.systems.hud.HudElement;
11+
import meteordevelopment.meteorclient.systems.hud.HudElementInfo;
12+
import meteordevelopment.meteorclient.systems.hud.HudRenderer;
13+
import meteordevelopment.meteorclient.utils.render.color.SettingColor;
14+
import net.minecraft.network.packet.c2s.play.ClientStatusC2SPacket;
15+
import net.minecraft.stat.Stats;
16+
import net.minecraft.util.Identifier;
17+
18+
import static meteordevelopment.meteorclient.MeteorClient.mc;
19+
20+
public class StatsHud extends HudElement {
21+
public static final HudElementInfo<StatsHud> INFO = new HudElementInfo<>(NerdAddon.HUD_GROUP, "stats", "Displays account and vanilla stat values.", StatsHud::new);
22+
23+
public enum Mode {
24+
Account,
25+
PlayerKills,
26+
MobKills,
27+
Deaths,
28+
Playtime,
29+
DamageDealt,
30+
DamageTaken
31+
}
32+
33+
static {
34+
INFO.addPreset("Account", hud -> hud.mode.set(Mode.Account));
35+
INFO.addPreset("Player Kills", hud -> hud.mode.set(Mode.PlayerKills));
36+
INFO.addPreset("Mob Kills", hud -> hud.mode.set(Mode.MobKills));
37+
INFO.addPreset("Deaths", hud -> hud.mode.set(Mode.Deaths));
38+
INFO.addPreset("Playtime", hud -> hud.mode.set(Mode.Playtime));
39+
INFO.addPreset("Damage Dealt", hud -> hud.mode.set(Mode.DamageDealt));
40+
INFO.addPreset("Damage Taken", hud -> hud.mode.set(Mode.DamageTaken));
41+
}
42+
43+
private final SettingGroup sgGeneral = settings.getDefaultGroup();
44+
private final SettingGroup sgColors = settings.createGroup("Colors");
45+
46+
private final Setting<Mode> mode = sgGeneral.add(new EnumSetting.Builder<Mode>()
47+
.name("mode")
48+
.description("Which account/stat value to display.")
49+
.defaultValue(Mode.Account)
50+
.build()
51+
);
52+
53+
private final Setting<Boolean> customLabel = sgGeneral.add(new BoolSetting.Builder()
54+
.name("custom-label")
55+
.description("Uses a custom label before the value.")
56+
.defaultValue(false)
57+
.build()
58+
);
59+
60+
private final Setting<String> label = sgGeneral.add(new StringSetting.Builder()
61+
.name("label")
62+
.description("Custom label.")
63+
.defaultValue("")
64+
.visible(customLabel::get)
65+
.build()
66+
);
67+
68+
private final Setting<Boolean> shadow = sgGeneral.add(new BoolSetting.Builder()
69+
.name("shadow")
70+
.description("Renders shadow behind text.")
71+
.defaultValue(true)
72+
.build()
73+
);
74+
75+
private final Setting<Boolean> syncStats = sgGeneral.add(new BoolSetting.Builder()
76+
.name("sync-stats")
77+
.description("Requests fresh vanilla stats from the server.")
78+
.defaultValue(true)
79+
.visible(() -> mode.get() != Mode.Account)
80+
.build()
81+
);
82+
83+
private final Setting<SettingColor> labelColor = sgColors.add(new ColorSetting.Builder()
84+
.name("label-color")
85+
.description("Color of the label.")
86+
.defaultValue(new SettingColor(175, 175, 175))
87+
.build()
88+
);
89+
90+
private final Setting<SettingColor> valueColor = sgColors.add(new ColorSetting.Builder()
91+
.name("value-color")
92+
.description("Color of the stat value.")
93+
.defaultValue(new SettingColor(255, 255, 255))
94+
.build()
95+
);
96+
97+
public StatsHud() {
98+
super(INFO);
99+
}
100+
101+
@Override
102+
public void tick(HudRenderer renderer) {
103+
if (syncStats.get() && mode.get() != Mode.Account) requestStats();
104+
}
105+
106+
@Override
107+
public void render(HudRenderer renderer) {
108+
String left = getLabel();
109+
String right = getValue();
110+
boolean shadow = this.shadow.get();
111+
double leftWidth = renderer.textWidth(left, shadow);
112+
double rightWidth = renderer.textWidth(right, shadow);
113+
114+
setSize(leftWidth + rightWidth, renderer.textHeight(shadow));
115+
116+
double x = this.x;
117+
x = renderer.text(left, x, y, labelColor.get(), shadow);
118+
renderer.text(right, x, y, valueColor.get(), shadow);
119+
}
120+
121+
private String getLabel() {
122+
if (customLabel.get() && !label.get().isBlank()) return label.get() + ": ";
123+
124+
return switch (mode.get()) {
125+
case Account -> "Account: ";
126+
case PlayerKills -> "Kills: ";
127+
case MobKills -> "Mob Kills: ";
128+
case Deaths -> "Deaths: ";
129+
case Playtime -> "Playtime: ";
130+
case DamageDealt -> "Damage Dealt: ";
131+
case DamageTaken -> "Damage Taken: ";
132+
};
133+
}
134+
135+
private String getValue() {
136+
if (mc.player == null) return mode.get() == Mode.Account ? "N/A" : "0";
137+
138+
return switch (mode.get()) {
139+
case Account -> mc.getSession().getUsername();
140+
case PlayerKills -> Integer.toString(getCustomStat(Stats.PLAYER_KILLS));
141+
case MobKills -> Integer.toString(getCustomStat(Stats.MOB_KILLS));
142+
case Deaths -> Integer.toString(getCustomStat(Stats.DEATHS));
143+
case Playtime -> formatPlaytime(getCustomStat(Stats.PLAY_TIME));
144+
case DamageDealt -> formatDamage(getCustomStat(Stats.DAMAGE_DEALT));
145+
case DamageTaken -> formatDamage(getCustomStat(Stats.DAMAGE_TAKEN));
146+
};
147+
}
148+
149+
private int getCustomStat(Identifier id) {
150+
return mc.player.getStatHandler().getStat(Stats.CUSTOM.getOrCreateStat(id));
151+
}
152+
153+
private void requestStats() {
154+
if (mc.getNetworkHandler() == null) return;
155+
156+
mc.getNetworkHandler().sendPacket(new ClientStatusC2SPacket(ClientStatusC2SPacket.Mode.REQUEST_STATS));
157+
}
158+
159+
private String formatDamage(int raw) {
160+
return String.format("%.1f", raw / 10.0);
161+
}
162+
163+
private String formatPlaytime(int ticks) {
164+
long seconds = ticks / 20L;
165+
long hours = seconds / 3600L;
166+
long minutes = (seconds % 3600L) / 60L;
167+
long secs = seconds % 60L;
168+
169+
if (hours > 0) return String.format("%dh %02dm", hours, minutes);
170+
if (minutes > 0) return String.format("%dm %02ds", minutes, secs);
171+
return secs + "s";
172+
}
173+
}

0 commit comments

Comments
 (0)