|
| 1 | +/* |
| 2 | + * Copyright (c) 2014-2026 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 net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; |
| 11 | +import net.minecraft.world.InteractionHand; |
| 12 | +import net.minecraft.world.entity.Entity; |
| 13 | +import net.minecraft.world.entity.projectile.ShulkerBullet; |
| 14 | +import net.minecraft.world.entity.projectile.hurtingprojectile.Fireball; |
| 15 | +import net.minecraft.world.phys.AABB; |
| 16 | +import net.minecraft.world.phys.Vec3; |
| 17 | +import net.wurstclient.Category; |
| 18 | +import net.wurstclient.SearchTags; |
| 19 | +import net.wurstclient.events.HandleInputListener; |
| 20 | +import net.wurstclient.events.UpdateListener; |
| 21 | +import net.wurstclient.hack.Hack; |
| 22 | +import net.wurstclient.settings.CheckboxSetting; |
| 23 | +import net.wurstclient.settings.SliderSetting; |
| 24 | +import net.wurstclient.util.Rotation; |
| 25 | +import net.wurstclient.util.RotationUtils; |
| 26 | + |
| 27 | +@SearchTags({"anti projectile", "projectile puncher", "anti fireball", |
| 28 | + "anti shulker bullet"}) |
| 29 | +public final class AntiProjectileHack extends Hack |
| 30 | + implements UpdateListener, HandleInputListener |
| 31 | +{ |
| 32 | + private final SliderSetting range = new SliderSetting("Range", 4, 3, 6, |
| 33 | + 0.25, SliderSetting.ValueDisplay.DECIMAL.withSuffix(" blocks")); |
| 34 | + private final SliderSetting cooldown = new SliderSetting("Cooldown", 4, 0, |
| 35 | + 20, 1, SliderSetting.ValueDisplay.INTEGER.withSuffix(" ticks")); |
| 36 | + private final CheckboxSetting swing = |
| 37 | + new CheckboxSetting("Swing hand", true); |
| 38 | + private final CheckboxSetting ignoreInventory = |
| 39 | + new CheckboxSetting("Ignore inventory", true); |
| 40 | + private final CheckboxSetting silentAim = new CheckboxSetting("Silent aim", |
| 41 | + "Faces incoming projectiles server-side like Killaura, without turning your camera.", |
| 42 | + true); |
| 43 | + |
| 44 | + private int cooldownLeft; |
| 45 | + private Entity pendingTarget; |
| 46 | + |
| 47 | + public AntiProjectileHack() |
| 48 | + { |
| 49 | + super("AntiProjectile", |
| 50 | + "Punches incoming fireballs and shulker bullets back before they hit you.", |
| 51 | + false); |
| 52 | + setCategory(Category.COMBAT); |
| 53 | + addSetting(range); |
| 54 | + addSetting(cooldown); |
| 55 | + addSetting(swing); |
| 56 | + addSetting(ignoreInventory); |
| 57 | + addSetting(silentAim); |
| 58 | + } |
| 59 | + |
| 60 | + @Override |
| 61 | + protected void onEnable() |
| 62 | + { |
| 63 | + EVENTS.add(UpdateListener.class, this); |
| 64 | + EVENTS.add(HandleInputListener.class, this); |
| 65 | + } |
| 66 | + |
| 67 | + @Override |
| 68 | + protected void onDisable() |
| 69 | + { |
| 70 | + EVENTS.remove(UpdateListener.class, this); |
| 71 | + EVENTS.remove(HandleInputListener.class, this); |
| 72 | + cooldownLeft = 0; |
| 73 | + pendingTarget = null; |
| 74 | + } |
| 75 | + |
| 76 | + @Override |
| 77 | + public void onUpdate() |
| 78 | + { |
| 79 | + if(MC.player == null || MC.level == null || MC.player.isSpectator()) |
| 80 | + return; |
| 81 | + if(!ignoreInventory.isChecked() |
| 82 | + && MC.screen instanceof AbstractContainerScreen) |
| 83 | + return; |
| 84 | + if(cooldownLeft > 0) |
| 85 | + { |
| 86 | + cooldownLeft--; |
| 87 | + return; |
| 88 | + } |
| 89 | + |
| 90 | + Entity target = findTarget(); |
| 91 | + if(target == null) |
| 92 | + return; |
| 93 | + |
| 94 | + Vec3 aimPos = target.getBoundingBox().getCenter(); |
| 95 | + if(silentAim.isChecked()) |
| 96 | + WURST.getRotationFaker().faceVectorPacket(aimPos); |
| 97 | + else |
| 98 | + { |
| 99 | + Rotation aim = RotationUtils.getNeededRotations(aimPos); |
| 100 | + aim.applyToClientPlayer(); |
| 101 | + aim.sendPlayerLookPacket(); |
| 102 | + } |
| 103 | + |
| 104 | + pendingTarget = target; |
| 105 | + } |
| 106 | + |
| 107 | + @Override |
| 108 | + public void onHandleInput() |
| 109 | + { |
| 110 | + if(pendingTarget == null || MC.player == null || MC.gameMode == null) |
| 111 | + return; |
| 112 | + |
| 113 | + MC.gameMode.attack(MC.player, pendingTarget); |
| 114 | + if(swing.isChecked()) |
| 115 | + MC.player.swing(InteractionHand.MAIN_HAND); |
| 116 | + |
| 117 | + cooldownLeft = cooldown.getValueI(); |
| 118 | + pendingTarget = null; |
| 119 | + } |
| 120 | + |
| 121 | + private Entity findTarget() |
| 122 | + { |
| 123 | + double rangeSq = range.getValue() * range.getValue(); |
| 124 | + Entity best = null; |
| 125 | + double bestDist = Double.MAX_VALUE; |
| 126 | + |
| 127 | + for(Entity entity : MC.level.entitiesForRendering()) |
| 128 | + { |
| 129 | + if(!isSupportedProjectile(entity) || !isIncoming(entity)) |
| 130 | + continue; |
| 131 | + |
| 132 | + Vec3 nextPos = entity.position().add(entity.getDeltaMovement()); |
| 133 | + AABB nextBox = |
| 134 | + entity.getDimensions(entity.getPose()).makeBoundingBox(nextPos); |
| 135 | + double distSq = nextBox.distanceToSqr(MC.player.getEyePosition()); |
| 136 | + if(distSq > rangeSq || distSq >= bestDist) |
| 137 | + continue; |
| 138 | + |
| 139 | + best = entity; |
| 140 | + bestDist = distSq; |
| 141 | + } |
| 142 | + |
| 143 | + return best; |
| 144 | + } |
| 145 | + |
| 146 | + private boolean isSupportedProjectile(Entity entity) |
| 147 | + { |
| 148 | + return entity instanceof Fireball || entity instanceof ShulkerBullet; |
| 149 | + } |
| 150 | + |
| 151 | + private boolean isIncoming(Entity entity) |
| 152 | + { |
| 153 | + Vec3 velocity = entity.getDeltaMovement(); |
| 154 | + if(velocity.lengthSqr() < 1.0E-6) |
| 155 | + return false; |
| 156 | + |
| 157 | + Vec3 toPlayer = |
| 158 | + MC.player.getBoundingBox().getCenter().subtract(entity.position()); |
| 159 | + double dot = toPlayer.dot(velocity); |
| 160 | + boolean roughlyTowardsPlayer = |
| 161 | + dot > 0.5 * velocity.length() * toPlayer.length(); |
| 162 | + |
| 163 | + AABB expanded = |
| 164 | + MC.player.getBoundingBox().inflate(entity.getBbWidth() / 2.0); |
| 165 | + boolean willHit = !expanded.contains(entity.position()) && expanded |
| 166 | + .clip(entity.position(), entity.position().add(velocity.scale(20))) |
| 167 | + .isPresent(); |
| 168 | + |
| 169 | + return roughlyTowardsPlayer || willHit; |
| 170 | + } |
| 171 | +} |
0 commit comments