Skip to content

Commit de2ccc9

Browse files
committed
Fixes
1 parent ce49c99 commit de2ccc9

3 files changed

Lines changed: 149 additions & 10 deletions

File tree

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

Lines changed: 120 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@
2727
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
2828
import net.minecraft.resources.Identifier;
2929
import net.minecraft.world.entity.Entity;
30+
import net.minecraft.world.entity.monster.Creeper;
3031
import net.minecraft.world.entity.monster.Enemy;
3132
import net.minecraft.world.entity.player.Player;
33+
import net.minecraft.world.entity.projectile.arrow.AbstractArrow;
3234
import net.minecraft.world.item.ItemStack;
3335
import net.minecraft.world.item.Items;
3436
import net.minecraft.world.phys.AABB;
@@ -53,6 +55,7 @@ public final class UntouchableHack extends Hack
5355
private static final int DIRECTION_SAMPLES = 16;
5456
private static final long MACE_PACKET_CUE_MS = 500;
5557
private static final long PRIMED_SPEAR_MEMORY_MS = 1200;
58+
private static final long CHARGING_CREEPER_MEMORY_MS = 1200;
5659

5760
private enum KeepDistanceMode
5861
{
@@ -160,12 +163,20 @@ public String toString()
160163
private final CheckboxSetting avoidHostileMobs =
161164
new CheckboxSetting("Avoid hostile mobs",
162165
"description.wurst.setting.untouchable.avoid_hostile_mobs", false);
166+
private final CheckboxSetting onlyChargingCreepers = new CheckboxSetting(
167+
"Only charging creepers",
168+
"description.wurst.setting.untouchable.only_charging_creepers", false);
169+
private final CheckboxSetting avoidArrows =
170+
new CheckboxSetting("Avoid arrows",
171+
"description.wurst.setting.untouchable.avoid_arrows", true);
163172

164173
private final Map<Integer, Vec3> previousPositions = new HashMap<>();
165174
private final Map<Integer, MacePacketCue> macePacketCues =
166175
new ConcurrentHashMap<>();
167176
private final Map<Integer, Long> primedSpearCues =
168177
new ConcurrentHashMap<>();
178+
private final Map<Integer, Long> chargingCreeperCues =
179+
new ConcurrentHashMap<>();
169180
private int cooldownTicksLeft;
170181
private int statusTicksLeft;
171182
private ThreatType activeThreat;
@@ -194,6 +205,8 @@ public UntouchableHack()
194205
addSetting(teleportCooldown);
195206
addSetting(avoidDrops);
196207
addSetting(avoidHostileMobs);
208+
addSetting(onlyChargingCreepers);
209+
addSetting(avoidArrows);
197210
}
198211

199212
@Override
@@ -223,6 +236,7 @@ private void reset()
223236
previousPositions.clear();
224237
macePacketCues.clear();
225238
primedSpearCues.clear();
239+
chargingCreeperCues.clear();
226240
cooldownTicksLeft = 0;
227241
statusTicksLeft = 0;
228242
activeThreat = null;
@@ -449,6 +463,7 @@ public void onUpdate()
449463
}
450464

451465
rememberPrimedSpears();
466+
rememberChargingCreepers();
452467
if(cooldownTicksLeft > 0)
453468
cooldownTicksLeft--;
454469

@@ -533,13 +548,35 @@ private Threat findMostUrgentThreat()
533548
if(!(entity instanceof Enemy) || entity == MC.player
534549
|| !entity.isAlive())
535550
continue;
551+
if(shouldSuppressDodging(entity.position()))
552+
continue;
553+
if(onlyChargingCreepers.isChecked()
554+
&& !isChargingCreeper(entity))
555+
continue;
536556

537557
Threat threat = getHostileMobThreat(entity);
538558
if(threat != null
539559
&& (best == null || threat.urgency > best.urgency))
540560
best = threat;
541561
}
542562
}
563+
if(avoidArrows.isChecked())
564+
{
565+
for(Entity entity : MC.level.entitiesForRendering())
566+
{
567+
if(!(entity instanceof AbstractArrow arrow) || !arrow.isAlive()
568+
|| arrow.getOwner() == MC.player)
569+
continue;
570+
Entity owner = arrow.getOwner();
571+
if(owner instanceof Player player && isIgnoredPlayer(player))
572+
continue;
573+
574+
Threat threat = getArrowThreat(arrow);
575+
if(threat != null
576+
&& (best == null || threat.urgency > best.urgency))
577+
best = threat;
578+
}
579+
}
543580
return best;
544581
}
545582

@@ -818,6 +855,59 @@ private Threat getHostileMobThreat(Entity mob)
818855
250 + (playerDistance.getValue() - distance) * 10);
819856
}
820857

858+
private Threat getArrowThreat(AbstractArrow arrow)
859+
{
860+
if(MC.player == null || MC.level == null)
861+
return null;
862+
863+
Vec3 playerCenter = MC.player.getBoundingBox().getCenter();
864+
Vec3 arrowCenter = arrow.getBoundingBox().getCenter();
865+
Vec3 velocity = getObservedVelocity(arrow);
866+
if(velocity.lengthSqr() < 0.01)
867+
velocity = arrow.getDeltaMovement();
868+
if(velocity.lengthSqr() < 0.01)
869+
return null;
870+
871+
Vec3 toPlayer = playerCenter.subtract(arrowCenter);
872+
if(velocity.dot(toPlayer) <= 0)
873+
return null;
874+
875+
double ticks = reactionTicks.getValue() + 2;
876+
Vec3 end = arrowCenter.add(velocity.scale(ticks));
877+
double missDistance = distanceToSegment(playerCenter, arrowCenter, end);
878+
double effectiveRange = Math.max(detectionRange.getValue(),
879+
velocity.length() * ticks + reachAllowance.getValue());
880+
if(arrowCenter.distanceToSqr(playerCenter) > square(effectiveRange)
881+
|| missDistance > reachAllowance.getValue() + 0.75)
882+
return null;
883+
884+
double urgency =
885+
110 + Math.max(0, 6 - arrowCenter.distanceTo(playerCenter)) * 12
886+
- missDistance * 5;
887+
return new Threat(ThreatType.ARROW, arrowCenter, end, urgency);
888+
}
889+
890+
private boolean isChargingCreeper(Entity entity)
891+
{
892+
if(!(entity instanceof Creeper creeper))
893+
return false;
894+
895+
return isChargingCreeper(creeper);
896+
}
897+
898+
private boolean isChargingCreeper(Creeper creeper)
899+
{
900+
if(creeper == null || !creeper.isAlive())
901+
return false;
902+
903+
if(creeper.getSwellDir() > 0 || creeper.isIgnited()
904+
|| creeper.isPowered())
905+
return true;
906+
907+
Long expiry = chargingCreeperCues.get(creeper.getId());
908+
return expiry != null && expiry >= System.currentTimeMillis();
909+
}
910+
821911
private Vec3 chooseDodgeDestination(Threat threat)
822912
{
823913
Vec3 playerPos = MC.player.position();
@@ -833,12 +923,16 @@ private Vec3 chooseDodgeDestination(Threat threat)
833923

834924
ArrayList<DodgeCandidate> candidates = new ArrayList<>();
835925
int verticalRange = verticalScan.getValueI();
836-
double maxHorizontal = scanDistance.getValue();
926+
double maxHorizontal =
927+
threat.type == ThreatType.ARROW ? 1.0 : scanDistance.getValue();
928+
double minHorizontal = threat.type == ThreatType.ARROW ? 1.0 : 0.5;
929+
double horizontalStep = threat.type == ThreatType.ARROW ? 1.0 : 0.5;
837930

838931
for(int yOffset = -verticalRange; yOffset <= verticalRange; yOffset++)
839932
{
840-
for(double distance = maxHorizontal; distance >= 0.5; distance -=
841-
0.5)
933+
for(double distance =
934+
maxHorizontal; distance >= minHorizontal; distance -=
935+
horizontalStep)
842936
{
843937
for(int i = 0; i < DIRECTION_SAMPLES; i++)
844938
{
@@ -966,6 +1060,24 @@ private void rememberPrimedSpears()
9661060
primedSpearCues.values().removeIf(expiry -> expiry < now);
9671061
}
9681062

1063+
private void rememberChargingCreepers()
1064+
{
1065+
if(MC.level == null)
1066+
return;
1067+
1068+
long now = System.currentTimeMillis();
1069+
for(Entity entity : MC.level.entitiesForRendering())
1070+
{
1071+
if(!(entity instanceof Creeper creeper) || !creeper.isAlive())
1072+
continue;
1073+
if(creeper.getSwellDir() > 0 || creeper.isIgnited()
1074+
|| creeper.isPowered())
1075+
chargingCreeperCues.put(creeper.getId(),
1076+
now + CHARGING_CREEPER_MEMORY_MS);
1077+
}
1078+
chargingCreeperCues.values().removeIf(expiry -> expiry < now);
1079+
}
1080+
9691081
private boolean wasRecentlyPrimed(Player player)
9701082
{
9711083
Long expiry = primedSpearCues.get(player.getId());
@@ -982,13 +1094,13 @@ private void prunePacketCues()
9821094
macePacketCues.values().removeIf(cue -> cue.timeMs < cutoff);
9831095
}
9841096

985-
private Vec3 getObservedVelocity(Player player)
1097+
private Vec3 getObservedVelocity(Entity entity)
9861098
{
987-
Vec3 networkVelocity = player.getDeltaMovement();
988-
Vec3 previous = previousPositions.get(player.getId());
1099+
Vec3 networkVelocity = entity.getDeltaMovement();
1100+
Vec3 previous = previousPositions.get(entity.getId());
9891101
if(previous == null)
9901102
return networkVelocity;
991-
Vec3 observed = player.position().subtract(previous);
1103+
Vec3 observed = entity.position().subtract(previous);
9921104
return observed.lengthSqr() > networkVelocity.lengthSqr() ? observed
9931105
: networkVelocity;
9941106
}
@@ -1160,6 +1272,7 @@ private enum ThreatType
11601272
SPEAR("Spear!"),
11611273
SWORD("Sword!"),
11621274
AXE("Axe!"),
1275+
ARROW("Arrow"),
11631276
MOB("Mob"),
11641277
SPACING("Spacing");
11651278

src/main/java/net/wurstclient/navigator/NavigatorFeatureScreen.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -605,10 +605,8 @@ protected void onRender(GuiGraphicsExtractor context, int mouseX,
605605
partialTicks);
606606
}
607607
matrixStack.popMatrix();
608-
context.disableScissor();
609-
context.enableScissor(bgx1, bgy1, bgx2, bgy3);
610-
611608
// buttons
609+
context.disableScissor();
612610
activeButton = null;
613611
for(ButtonData buttonData : buttonDatas)
614612
{
@@ -694,6 +692,32 @@ else if(mouseX >= bx1 && mouseX <= bx2 && mouseY >= by1
694692
txtColor, false);
695693
}
696694

695+
// Fallback primary action button rendering.
696+
//
697+
// In some builds the vanilla widget paint path can be visually absent
698+
// even though the button still receives clicks. Drawing the button
699+
// directly here keeps the enable/disable control visible.
700+
if(primaryButton != null)
701+
{
702+
int bx1 = primaryButton.getX();
703+
int bx2 = bx1 + primaryButton.getWidth();
704+
int by1 = primaryButton.getY();
705+
int by2 = by1 + 18;
706+
boolean hovering = mouseX >= bx1 && mouseX <= bx2 && mouseY >= by1
707+
&& mouseY <= by2;
708+
int buttonColor;
709+
if(feature.isEnabled())
710+
buttonColor = hovering ? 0x9000FF00 : 0x9000E000;
711+
else
712+
buttonColor = hovering ? 0x90606060 : 0x90404040;
713+
drawBox(context, bx1, by1, bx2, by2, buttonColor);
714+
String buttonText = primaryButton.getMessage().getString();
715+
context.guiRenderState.up();
716+
context.text(minecraft.font, buttonText,
717+
(bx1 + bx2 - minecraft.font.width(buttonText)) / 2, by1 + 5,
718+
txtColor, false);
719+
}
720+
697721
// popups & tooltip
698722
gui.closePopupsOutsideArea(window, bgx1, bgy1, bgx2, bgy3);
699723
gui.renderPopups(context, mouseX, mouseY);

src/main/resources/assets/wurst/translations/en_us.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
"hack.wurst.untouchable": "Untouchable",
141141
"description.wurst.hack.untouchable": "Dodges threatening mace, spear, sword, and axe attacks by teleporting to safer nearby positions.",
142142
"description.wurst.setting.untouchable.avoid_drops": "Rejects dodge destinations without solid ground when you are standing on the ground.",
143+
"description.wurst.setting.untouchable.avoid_arrows": "Also dodges incoming arrows and other arrow-type projectiles.",
143144
"description.wurst.setting.untouchable.avoid_hostile_mobs": "Also dodges hostile mobs like zombies, skeletons, and other aggressive enemies.",
144145
"description.wurst.setting.untouchable.auto_distance_on_damage": "Automatically switches Keep distance mode to Always when another player damages you.",
145146
"description.wurst.setting.untouchable.auto_distance_on_totem_pop": "Automatically switches Keep distance mode to Always when you pop your own totem of undying.",
@@ -151,6 +152,7 @@
151152
"description.wurst.setting.untouchable.keep_distance": "Enables the distance-keeping behavior for nearby players.",
152153
"description.wurst.setting.untouchable.keep_distance_mode": "Always keeps the configured distance, or only while the hack is already dodging a weapon attack.",
153154
"description.wurst.setting.untouchable.move_pause_mode": "Stops dodging while you press any movement key, or only when you are moving toward the current target.",
155+
"description.wurst.setting.untouchable.only_charging_creepers": "When avoiding hostile mobs, only dodge creepers that are already charging to explode.",
154156
"description.wurst.setting.untouchable.only_primed_spears": "Only dodges spears after they are fully primed or were just primed.",
155157
"description.wurst.setting.untouchable.player_distance": "Minimum distance to keep from other players.",
156158
"description.wurst.setting.untouchable.prediction": "How many ticks ahead to predict incoming attacks.",

0 commit comments

Comments
 (0)