|
| 1 | +/* |
| 2 | + * Copyright (c) 2014-2025 Wurst-Imperium and contributors. |
| 3 | + * |
| 4 | + * This source code is subject to the terms of the GNU General Public |
| 5 | + * License, version 3. If a copy of the GPL was not distributed with this |
| 6 | + * file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt |
| 7 | + */ |
| 8 | +package net.wurstclient.hacks; |
| 9 | + |
| 10 | +import java.awt.Color; |
| 11 | +import java.util.ArrayList; |
| 12 | +import java.util.List; |
| 13 | + |
| 14 | +import net.minecraft.client.util.math.MatrixStack; |
| 15 | +import net.minecraft.entity.Entity; |
| 16 | +import net.minecraft.entity.LivingEntity; |
| 17 | +import net.minecraft.entity.player.PlayerEntity; |
| 18 | +import net.minecraft.entity.projectile.TridentEntity; |
| 19 | +import net.minecraft.item.Items; |
| 20 | +import net.minecraft.item.ItemStack; |
| 21 | +import net.minecraft.util.Arm; |
| 22 | +import net.minecraft.util.Hand; |
| 23 | +import net.minecraft.util.math.Box; |
| 24 | +import net.minecraft.util.math.Vec3d; |
| 25 | +import net.wurstclient.Category; |
| 26 | +import net.wurstclient.SearchTags; |
| 27 | +import net.wurstclient.events.CameraTransformViewBobbingListener; |
| 28 | +import net.wurstclient.events.RenderListener; |
| 29 | +import net.wurstclient.events.UpdateListener; |
| 30 | +import net.wurstclient.hack.Hack; |
| 31 | +import net.wurstclient.settings.CheckboxSetting; |
| 32 | +import net.wurstclient.settings.ColorSetting; |
| 33 | +import net.wurstclient.settings.EspBoxSizeSetting; |
| 34 | +import net.wurstclient.settings.EspStyleSetting; |
| 35 | +import net.wurstclient.util.EntityUtils; |
| 36 | +import net.wurstclient.util.RenderUtils; |
| 37 | + |
| 38 | +@SearchTags({"trident esp", "TridentTracers", "trident tracers"}) |
| 39 | +public final class TridentEspHack extends Hack implements UpdateListener, |
| 40 | + CameraTransformViewBobbingListener, RenderListener |
| 41 | +{ |
| 42 | + private final EspStyleSetting style = new EspStyleSetting(); |
| 43 | + private final EspBoxSizeSetting boxSize = new EspBoxSizeSetting( |
| 44 | + "\u00a7lAccurate\u00a7r mode shows the exact hitbox.\n" |
| 45 | + + "\u00a7lFancy\u00a7r mode shows larger boxes that look better."); |
| 46 | + |
| 47 | + // Color controls (like Search): single fixed color or rainbow |
| 48 | + private final CheckboxSetting useFixedColor = |
| 49 | + new CheckboxSetting("Use fixed color", |
| 50 | + "Enable to use a fixed color instead of rainbow.", false); |
| 51 | + private final ColorSetting fixedColor = new ColorSetting("Fixed color", |
| 52 | + "Color used when \"Use fixed color\" is enabled.", Color.CYAN); |
| 53 | + |
| 54 | + // Distinguish by owner type (self, other player, mob) |
| 55 | + private final CheckboxSetting colorByOwner = new CheckboxSetting( |
| 56 | + "Color by owner type", |
| 57 | + "When enabled, uses different colors depending on who threw/holds the trident.", |
| 58 | + false); |
| 59 | + private final ColorSetting selfColor = new ColorSetting("Your tridents", |
| 60 | + "Color for your own thrown/held tridents.", new Color(0x55FF55)); |
| 61 | + private final ColorSetting otherPlayerColor = |
| 62 | + new ColorSetting("Other players", |
| 63 | + "Color for tridents from other players.", new Color(0xFF5555)); |
| 64 | + private final ColorSetting mobColor = new ColorSetting("Mobs", |
| 65 | + "Color for tridents from mobs.", new Color(0xFFFF55)); |
| 66 | + |
| 67 | + // Include tridents currently held by others |
| 68 | + private final CheckboxSetting includeHeld = new CheckboxSetting( |
| 69 | + "Highlight held tridents", |
| 70 | + "Also highlight when a trident is currently held by another player or mob.", |
| 71 | + false); |
| 72 | + |
| 73 | + // Cached per-tick results |
| 74 | + private final ArrayList<TridentEntity> thrown = new ArrayList<>(); |
| 75 | + private final ArrayList<LivingEntity> holders = new ArrayList<>(); |
| 76 | + |
| 77 | + public TridentEspHack() |
| 78 | + { |
| 79 | + super("TridentESP"); |
| 80 | + setCategory(Category.RENDER); |
| 81 | + addSetting(style); |
| 82 | + addSetting(boxSize); |
| 83 | + addSetting(useFixedColor); |
| 84 | + addSetting(fixedColor); |
| 85 | + addSetting(colorByOwner); |
| 86 | + addSetting(selfColor); |
| 87 | + addSetting(otherPlayerColor); |
| 88 | + addSetting(mobColor); |
| 89 | + addSetting(includeHeld); |
| 90 | + } |
| 91 | + |
| 92 | + @Override |
| 93 | + protected void onEnable() |
| 94 | + { |
| 95 | + EVENTS.add(UpdateListener.class, this); |
| 96 | + EVENTS.add(CameraTransformViewBobbingListener.class, this); |
| 97 | + EVENTS.add(RenderListener.class, this); |
| 98 | + } |
| 99 | + |
| 100 | + @Override |
| 101 | + protected void onDisable() |
| 102 | + { |
| 103 | + EVENTS.remove(UpdateListener.class, this); |
| 104 | + EVENTS.remove(CameraTransformViewBobbingListener.class, this); |
| 105 | + EVENTS.remove(RenderListener.class, this); |
| 106 | + thrown.clear(); |
| 107 | + holders.clear(); |
| 108 | + } |
| 109 | + |
| 110 | + @Override |
| 111 | + public void onUpdate() |
| 112 | + { |
| 113 | + thrown.clear(); |
| 114 | + holders.clear(); |
| 115 | + |
| 116 | + for(Entity e : MC.world.getEntities()) |
| 117 | + { |
| 118 | + if(e instanceof TridentEntity) |
| 119 | + thrown.add((TridentEntity)e); |
| 120 | + if(includeHeld.isChecked() && e instanceof LivingEntity) |
| 121 | + { |
| 122 | + LivingEntity le = (LivingEntity)e; |
| 123 | + if(le == MC.player) |
| 124 | + continue; // "another player or a mob" only |
| 125 | + if(isHoldingTrident(le)) |
| 126 | + holders.add(le); |
| 127 | + } |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + private boolean isHoldingTrident(LivingEntity e) |
| 132 | + { |
| 133 | + ItemStack main = e.getMainHandStack(); |
| 134 | + ItemStack off = e.getOffHandStack(); |
| 135 | + return (main != null && main.getItem() == Items.TRIDENT) |
| 136 | + || (off != null && off.getItem() == Items.TRIDENT); |
| 137 | + } |
| 138 | + |
| 139 | + @Override |
| 140 | + public void onCameraTransformViewBobbing( |
| 141 | + CameraTransformViewBobbingEvent event) |
| 142 | + { |
| 143 | + if(style.hasLines()) |
| 144 | + event.cancel(); |
| 145 | + } |
| 146 | + |
| 147 | + @Override |
| 148 | + public void onRender(MatrixStack matrixStack, float partialTicks) |
| 149 | + { |
| 150 | + if(!style.hasBoxes() && !style.hasLines()) |
| 151 | + return; |
| 152 | + |
| 153 | + if(colorByOwner.isChecked()) |
| 154 | + renderByOwner(matrixStack, partialTicks); |
| 155 | + else |
| 156 | + renderSingleColor(matrixStack, partialTicks); |
| 157 | + } |
| 158 | + |
| 159 | + private void renderSingleColor(MatrixStack matrixStack, float partialTicks) |
| 160 | + { |
| 161 | + int lineColor; |
| 162 | + if(useFixedColor.isChecked()) |
| 163 | + lineColor = fixedColor.getColorI(0x80); |
| 164 | + else |
| 165 | + lineColor = |
| 166 | + RenderUtils.toIntColor(RenderUtils.getRainbowColor(), 0.5F); |
| 167 | + |
| 168 | + if(style.hasBoxes()) |
| 169 | + { |
| 170 | + List<Box> boxes = new ArrayList<>(); |
| 171 | + for(TridentEntity t : thrown) |
| 172 | + boxes.add( |
| 173 | + applyExtraSize(EntityUtils.getLerpedBox(t, partialTicks))); |
| 174 | + for(LivingEntity h : holders) |
| 175 | + { |
| 176 | + Box handBox = getHeldTridentBox(h, partialTicks); |
| 177 | + if(handBox != null) |
| 178 | + boxes.add(applyExtraSize(handBox)); |
| 179 | + } |
| 180 | + if(!boxes.isEmpty()) |
| 181 | + RenderUtils.drawOutlinedBoxes(matrixStack, boxes, lineColor, |
| 182 | + false); |
| 183 | + } |
| 184 | + |
| 185 | + if(style.hasLines()) |
| 186 | + { |
| 187 | + List<Vec3d> ends = new ArrayList<>(); |
| 188 | + for(TridentEntity t : thrown) |
| 189 | + ends.add(EntityUtils.getLerpedBox(t, partialTicks).getCenter()); |
| 190 | + for(LivingEntity h : holders) |
| 191 | + { |
| 192 | + Vec3d hand = getHeldTridentPos(h, partialTicks); |
| 193 | + if(hand != null) |
| 194 | + ends.add(hand); |
| 195 | + } |
| 196 | + if(!ends.isEmpty()) |
| 197 | + RenderUtils.drawTracers(matrixStack, partialTicks, ends, |
| 198 | + lineColor, false); |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + private void renderByOwner(MatrixStack matrixStack, float partialTicks) |
| 203 | + { |
| 204 | + ArrayList<Box> selfBoxes = new ArrayList<>(); |
| 205 | + ArrayList<Box> playerBoxes = new ArrayList<>(); |
| 206 | + ArrayList<Box> mobBoxes = new ArrayList<>(); |
| 207 | + ArrayList<Vec3d> selfEnds = new ArrayList<>(); |
| 208 | + ArrayList<Vec3d> playerEnds = new ArrayList<>(); |
| 209 | + ArrayList<Vec3d> mobEnds = new ArrayList<>(); |
| 210 | + |
| 211 | + for(TridentEntity t : thrown) |
| 212 | + { |
| 213 | + Entity owner = t.getOwner(); |
| 214 | + Box box = applyExtraSize(EntityUtils.getLerpedBox(t, partialTicks)); |
| 215 | + Vec3d end = box.getCenter(); |
| 216 | + if(owner instanceof PlayerEntity) |
| 217 | + { |
| 218 | + if(owner == MC.player) |
| 219 | + { |
| 220 | + selfBoxes.add(box); |
| 221 | + selfEnds.add(end); |
| 222 | + }else |
| 223 | + { |
| 224 | + playerBoxes.add(box); |
| 225 | + playerEnds.add(end); |
| 226 | + } |
| 227 | + }else if(owner instanceof LivingEntity) |
| 228 | + { |
| 229 | + mobBoxes.add(box); |
| 230 | + mobEnds.add(end); |
| 231 | + }else |
| 232 | + { |
| 233 | + // Unknown owner: treat as other player color for visibility |
| 234 | + playerBoxes.add(box); |
| 235 | + playerEnds.add(end); |
| 236 | + } |
| 237 | + } |
| 238 | + |
| 239 | + if(includeHeld.isChecked()) |
| 240 | + { |
| 241 | + for(LivingEntity h : holders) |
| 242 | + { |
| 243 | + Box handBox = getHeldTridentBox(h, partialTicks); |
| 244 | + Vec3d end = getHeldTridentPos(h, partialTicks); |
| 245 | + if(handBox == null || end == null) |
| 246 | + continue; |
| 247 | + if(h instanceof PlayerEntity) |
| 248 | + { |
| 249 | + playerBoxes.add(applyExtraSize(handBox)); |
| 250 | + playerEnds.add(end); |
| 251 | + }else |
| 252 | + { |
| 253 | + mobBoxes.add(applyExtraSize(handBox)); |
| 254 | + mobEnds.add(end); |
| 255 | + } |
| 256 | + } |
| 257 | + } |
| 258 | + |
| 259 | + // Draw grouped to allow per-group colors |
| 260 | + int selfCol = selfColor.getColorI(0x80); |
| 261 | + int playerCol = otherPlayerColor.getColorI(0x80); |
| 262 | + int mobCol = mobColor.getColorI(0x80); |
| 263 | + |
| 264 | + if(style.hasBoxes()) |
| 265 | + { |
| 266 | + if(!selfBoxes.isEmpty()) |
| 267 | + RenderUtils.drawOutlinedBoxes(matrixStack, selfBoxes, selfCol, |
| 268 | + false); |
| 269 | + if(!playerBoxes.isEmpty()) |
| 270 | + RenderUtils.drawOutlinedBoxes(matrixStack, playerBoxes, |
| 271 | + playerCol, false); |
| 272 | + if(!mobBoxes.isEmpty()) |
| 273 | + RenderUtils.drawOutlinedBoxes(matrixStack, mobBoxes, mobCol, |
| 274 | + false); |
| 275 | + } |
| 276 | + |
| 277 | + if(style.hasLines()) |
| 278 | + { |
| 279 | + if(!selfEnds.isEmpty()) |
| 280 | + RenderUtils.drawTracers(matrixStack, partialTicks, selfEnds, |
| 281 | + selfCol, false); |
| 282 | + if(!playerEnds.isEmpty()) |
| 283 | + RenderUtils.drawTracers(matrixStack, partialTicks, playerEnds, |
| 284 | + playerCol, false); |
| 285 | + if(!mobEnds.isEmpty()) |
| 286 | + RenderUtils.drawTracers(matrixStack, partialTicks, mobEnds, |
| 287 | + mobCol, false); |
| 288 | + } |
| 289 | + } |
| 290 | + |
| 291 | + private Box applyExtraSize(Box box) |
| 292 | + { |
| 293 | + double extra = boxSize.getExtraSize() / 2.0; |
| 294 | + return box.offset(0, extra, 0).expand(extra); |
| 295 | + } |
| 296 | + |
| 297 | + // New helpers: approximate where the held trident is, and make a small box |
| 298 | + // there |
| 299 | + private Vec3d getHeldTridentPos(LivingEntity e, float partialTicks) |
| 300 | + { |
| 301 | + Hand hand = null; |
| 302 | + if(!e.getMainHandStack().isEmpty() |
| 303 | + && e.getMainHandStack().isOf(Items.TRIDENT)) |
| 304 | + hand = Hand.MAIN_HAND; |
| 305 | + else if(!e.getOffHandStack().isEmpty() |
| 306 | + && e.getOffHandStack().isOf(Items.TRIDENT)) |
| 307 | + hand = Hand.OFF_HAND; |
| 308 | + if(hand == null) |
| 309 | + return null; |
| 310 | + |
| 311 | + // Base position at entity feet (lerped), then add eye height - 0.1 |
| 312 | + Vec3d base = EntityUtils.getLerpedPos(e, partialTicks); |
| 313 | + double yawRad = Math.toRadians(e.getYaw()); |
| 314 | + |
| 315 | + // Determine which side the given hand is on. |
| 316 | + Arm mainArm = Arm.RIGHT; |
| 317 | + if(e instanceof PlayerEntity pe) |
| 318 | + mainArm = pe.getMainArm(); |
| 319 | + boolean rightSide = (mainArm == Arm.RIGHT && hand == Hand.MAIN_HAND) |
| 320 | + || (mainArm == Arm.LEFT && hand == Hand.OFF_HAND); |
| 321 | + double side = rightSide ? -1 : 1; |
| 322 | + |
| 323 | + double eyeH = e.getEyeHeight(e.getPose()); |
| 324 | + double offX = Math.cos(yawRad) * 0.16 * side; |
| 325 | + double offY = eyeH - 0.1; |
| 326 | + double offZ = Math.sin(yawRad) * 0.16 * side; |
| 327 | + return base.add(offX, offY, offZ); |
| 328 | + } |
| 329 | + |
| 330 | + private Box getHeldTridentBox(LivingEntity e, float partialTicks) |
| 331 | + { |
| 332 | + Vec3d c = getHeldTridentPos(e, partialTicks); |
| 333 | + if(c == null) |
| 334 | + return null; |
| 335 | + // Small cube around hand |
| 336 | + double r = 0.18; // half-size |
| 337 | + return new Box(c.x - r, c.y - r, c.z - r, c.x + r, c.y + r, c.z + r); |
| 338 | + } |
| 339 | +} |
0 commit comments