Skip to content

Commit b9a2f78

Browse files
committed
XP Orbs Added to ItemESP and ItemHandler
1 parent f4e0893 commit b9a2f78

File tree

5 files changed

+445
-51
lines changed

5 files changed

+445
-51
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ Because so many of these mods are entirely original, I plan to release some of t
297297
- Adjustable font scale setting (scales text and popup icon size).
298298
- Adjustable Popup HUD display offset X/Y
299299
- Respects ItemESP ignored list (optional) and provides link to edit the list.
300+
- Detects XP Orbs and shows the exact XP each orb will give
300301

301302
![Popup](https://i.imgur.com/W97borj.png)
302303
![GUI](https://i.imgur.com/eunLgVr.png)
@@ -318,6 +319,7 @@ Highlights dropped, equipped, and framed items with powerful filters and customi
318319
- Robust parsing: lists accept unknown entries as keywords (safe parsing via `Identifier.tryParse`).
319320
- Ignore list: using keywords or specific item names you can exclude items from being highlighted entirely.
320321
- Entity ignore: Can ignore other players, armor stands etc
322+
- Detects XP Orbs and allows for different coloring or disabling entirely.
321323

322324
Examples:
323325
- Highlight skulls → Item ID: `minecraft:player_head`, special color: magenta, outline-only ON.

src/main/java/net/wurstclient/hacks/ItemEspHack.java

Lines changed: 116 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import net.minecraft.world.entity.decoration.ArmorStand;
2323
import net.minecraft.world.entity.decoration.ItemFrame;
2424
import net.minecraft.world.entity.item.ItemEntity;
25+
import net.minecraft.world.entity.ExperienceOrb;
2526
import net.minecraft.world.entity.npc.Villager;
2627
import net.minecraft.world.entity.player.Player;
2728
import net.minecraft.world.item.Item;
@@ -55,6 +56,14 @@ private enum SpecialMode
5556
QUERY
5657
}
5758

59+
private enum XpMode
60+
{
61+
OFF,
62+
NORMAL,
63+
SPECIAL,
64+
RAINBOW
65+
}
66+
5867
private static final int MAX_SPECIAL_TEXT_LENGTH = 256;
5968
private final EspStyleSetting style = new EspStyleSetting();
6069

@@ -122,6 +131,11 @@ private enum SpecialMode
122131
new CheckboxSetting("Ignore villagers",
123132
"Won't highlight equipped special items on villagers.", false);
124133

134+
// XP orb handling mode
135+
private final net.wurstclient.settings.EnumSetting<XpMode> xpMode =
136+
new net.wurstclient.settings.EnumSetting<>("XP orb mode",
137+
XpMode.values(), XpMode.NORMAL);
138+
125139
// New: include item frames holding special items
126140
private final CheckboxSetting includeItemFrames =
127141
new CheckboxSetting("Highlight frames with special",
@@ -134,6 +148,7 @@ private enum SpecialMode
134148
false);
135149

136150
private final ArrayList<ItemEntity> items = new ArrayList<>();
151+
private final ArrayList<ExperienceOrb> xpOrbs = new ArrayList<>();
137152
// Above-ground filter
138153
private final CheckboxSetting onlyAboveGround =
139154
new CheckboxSetting("Above ground only",
@@ -164,6 +179,7 @@ public ItemEspHack()
164179
addSetting(specialRainbow);
165180
addSetting(specialColor);
166181
addSetting(outlineOnly);
182+
addSetting(xpMode);
167183
// ignored items
168184
addSetting(useIgnoredItems);
169185
addSetting(ignoredList);
@@ -223,11 +239,16 @@ public boolean isIgnoredId(String id)
223239
public void onUpdate()
224240
{
225241
items.clear();
242+
xpOrbs.clear();
226243
for(Entity entity : MC.level.entitiesForRendering())
227-
if(entity instanceof ItemEntity)
228-
items.add((ItemEntity)entity);
244+
{
245+
if(entity instanceof ItemEntity ie)
246+
items.add(ie);
247+
else if(entity instanceof ExperienceOrb xo)
248+
xpOrbs.add(xo);
249+
}
229250
// update count for HUD (clamped to 999)
230-
foundCount = Math.min(items.size(), 999);
251+
foundCount = Math.min(items.size() + xpOrbs.size(), 999);
231252
}
232253

233254
@Override
@@ -324,12 +345,22 @@ public void onRender(PoseStack matrixStack, float partialTicks)
324345
AABB box = EntityUtils.getLerpedBox(e, partialTicks)
325346
.move(0, extraSize, 0).inflate(extraSize);
326347
boolean isSpecial = isSpecial(stack);
327-
// check traced override from ItemHandlerHack
328-
String id =
329-
BuiltInRegistries.ITEM.getKey(stack.getItem()).toString();
348+
// check traced override from ItemHandlerHack (use synthetic id if
349+
// present)
350+
String id = net.wurstclient.util.ItemUtils.getStackId(stack);
330351
net.wurstclient.hacks.itemhandler.ItemHandlerHack ih =
331352
net.wurstclient.WurstClient.INSTANCE.getHax().itemHandlerHack;
332-
boolean isTraced = ih != null && ih.isTraced(id);
353+
boolean isTraced = false;
354+
if(ih != null && id != null)
355+
{
356+
isTraced = ih.isTraced(id);
357+
if(!isTraced
358+
&& net.wurstclient.util.ItemUtils.isSyntheticXp(stack))
359+
{
360+
int xp = net.wurstclient.util.ItemUtils.getXpAmount(stack);
361+
isTraced = ih.isTraced(id + ":xp:" + xp);
362+
}
363+
}
333364
visibleDrops++;
334365
if(isTraced)
335366
{
@@ -350,6 +381,63 @@ public void onRender(PoseStack matrixStack, float partialTicks)
350381
}
351382
foundCount = Math.min(visibleDrops, 999);
352383

384+
// Integrate XP orbs into boxes/ends as synthetic items
385+
for(ExperienceOrb orb : xpOrbs)
386+
{
387+
if(onlyAboveGround.isChecked()
388+
&& orb.getY() < aboveGroundY.getValue())
389+
continue;
390+
// synthetic proxy stack for rendering and UI
391+
net.minecraft.world.item.ItemStack stack =
392+
net.wurstclient.util.ItemUtils.createSyntheticXpStack(orb);
393+
if(isIgnored(stack))
394+
continue;
395+
Vec3 center =
396+
EntityUtils.getLerpedBox(orb, partialTicks).getCenter();
397+
AABB box = smallBoxAt(center);
398+
// decide how to render XP orbs based on user setting and trace
399+
XpMode mode = xpMode.getSelected();
400+
if(mode == XpMode.OFF)
401+
continue;
402+
String id = "minecraft:experience_orb";
403+
net.wurstclient.hacks.itemhandler.ItemHandlerHack ih =
404+
net.wurstclient.WurstClient.INSTANCE.getHax().itemHandlerHack;
405+
boolean isTraced = false;
406+
if(ih != null)
407+
{
408+
isTraced = ih.isTraced(id);
409+
int xp = net.wurstclient.util.ItemUtils.getXpAmount(stack);
410+
if(!isTraced)
411+
isTraced = ih.isTraced(id + ":xp:" + xp);
412+
}
413+
visibleDrops++;
414+
if(isTraced)
415+
{
416+
tracedBoxes.add(box);
417+
tracedEnds.add(center);
418+
}else
419+
{
420+
switch(mode)
421+
{
422+
case RAINBOW ->
423+
{
424+
tracedBoxes.add(box);
425+
tracedEnds.add(center);
426+
}
427+
case SPECIAL ->
428+
{
429+
specialBoxes.add(box);
430+
specialEnds.add(center);
431+
}
432+
default ->
433+
{
434+
normalBoxes.add(box);
435+
normalEnds.add(center);
436+
}
437+
}
438+
}
439+
}
440+
353441
// Item frames holding special items
354442
if(includeItemFrames.isChecked())
355443
{
@@ -520,20 +608,28 @@ private boolean isIgnored(ItemStack stack)
520608
return false;
521609
if(ignoredExactIds == null || ignoredExactIds.isEmpty())
522610
return false;
523-
ResourceLocation id = BuiltInRegistries.ITEM.getKey(stack.getItem());
611+
String id = net.wurstclient.util.ItemUtils.getStackId(stack);
524612
if(id == null)
525613
return false;
526-
return ignoredExactIds.contains(id.toString());
614+
if(ignoredExactIds != null && ignoredExactIds.contains(id))
615+
return true;
616+
// fallback: allow raw ignored list entries (useful for synthetic ids)
617+
for(String s : ignoredList.getItemNames())
618+
if(id.equalsIgnoreCase(s.trim()))
619+
return true;
620+
return false;
527621
}
528622

529623
private boolean isSpecial(ItemStack stack)
530624
{
531625
Item item = stack.getItem();
626+
String stackId = net.wurstclient.util.ItemUtils.getStackId(stack);
532627
switch(specialMode.getSelected())
533628
{
534629
case LIST:
535630
{
536-
String id = BuiltInRegistries.ITEM.getKey(item).toString();
631+
String id = stackId != null ? stackId
632+
: BuiltInRegistries.ITEM.getKey(item).toString();
537633
if(specialExactIds != null && specialExactIds.contains(id))
538634
return true;
539635
String localId =
@@ -554,7 +650,13 @@ private boolean isSpecial(ItemStack stack)
554650
return false;
555651
}
556652
case ITEM_ID:
557-
return itemMatchesId(item, specialItemId.getValue());
653+
{
654+
if(stackId != null && specialItemId.getValue() != null
655+
&& stackId
656+
.equalsIgnoreCase(specialItemId.getValue().trim()))
657+
return true;
658+
return itemMatchesId(item, specialItemId.getValue());
659+
}
558660
case QUERY:
559661
return itemOrStackMatchesQuery(item, stack,
560662
normalizeQuery(specialQuery.getValue()));
@@ -590,7 +692,9 @@ private boolean itemOrStackMatchesQuery(Item item, ItemStack stack,
590692
{
591693
if(normalizedQuery.isEmpty())
592694
return false;
593-
String fullId = BuiltInRegistries.ITEM.getKey(item).toString();
695+
String stackId = net.wurstclient.util.ItemUtils.getStackId(stack);
696+
String fullId = stackId != null ? stackId
697+
: BuiltInRegistries.ITEM.getKey(item).toString();
594698
String localId = fullId.contains(":")
595699
? fullId.substring(fullId.indexOf(":") + 1) : fullId;
596700
String localSpaced = localId.replace('_', ' ');

0 commit comments

Comments
 (0)