From 9f61a29be03d2faa1583fbae068062adbbc1652b Mon Sep 17 00:00:00 2001 From: AH Date: Wed, 27 Aug 2025 00:03:33 -0400 Subject: [PATCH 01/23] feat: pre-test player/"legit" aimbot impl --- .../dev/cigarette/agent/ZombiesAgent.java | 35 ++- .../java/dev/cigarette/config/Config.java | 3 +- .../cigarette/gui/widget/SliderWidget.java | 73 ++++++- .../java/dev/cigarette/lib/PlayerEntityL.java | 8 +- src/main/java/dev/cigarette/lib/ServerL.java | 76 +++++++ .../dev/cigarette/lib/WeaponSelector.java | 48 ++++ .../cigarette/module/combat/PlayerAimbot.java | 205 ++++++++++++++++++ 7 files changed, 437 insertions(+), 11 deletions(-) create mode 100644 src/main/java/dev/cigarette/lib/ServerL.java create mode 100644 src/main/java/dev/cigarette/module/combat/PlayerAimbot.java diff --git a/src/main/java/dev/cigarette/agent/ZombiesAgent.java b/src/main/java/dev/cigarette/agent/ZombiesAgent.java index 732f66f9..44791bdb 100644 --- a/src/main/java/dev/cigarette/agent/ZombiesAgent.java +++ b/src/main/java/dev/cigarette/agent/ZombiesAgent.java @@ -92,7 +92,7 @@ public static ZombiesAgent.ZombieTarget getBestTarget(ClientPlayerEntity player) return bestTarget; } - private Vec3d calculatePredictedPosition(ZombiesAgent.ZombieTarget zombie, ClientPlayerEntity player) { + public static Vec3d calculatePredictedPosition(ZombiesAgent.ZombieTarget zombie, ClientPlayerEntity player) { if (!Aimbot.INSTANCE.predictiveAim.getRawState()) { return zombie.entity.getPos().subtract(player.getPos().add(0, player.getEyeHeight(EntityPose.STANDING), 0)); } @@ -119,7 +119,36 @@ private Vec3d calculatePredictedPosition(ZombiesAgent.ZombieTarget zombie, Clien return predictedBodyPos.add(0, zombie.entity.getEyeHeight(zombie.entity.getPose()), 0); } - private Vec3d getPathfindingDirection(ZombiesAgent.ZombieTarget zombie, Vec3d zombiePos, Vec3d playerPos, Vec3d currentVelocity) { + public static Vec3d calculatePredictedPosition(ClientPlayerEntity target, ClientPlayerEntity player) { + Vec3d currentPos = target.getPos(); + Vec3d playerPos = player.getEyePos(); + Vec3d instantVelocity = target.getPos().subtract(target.lastX, target.lastY, target.lastZ); + + double xVelocity = instantVelocity.x * Aimbot.INSTANCE.predictionTicks.getRawState().intValue(); + double yVelocity = instantVelocity.y > LivingEntity.GRAVITY ? 0 : instantVelocity.y * (instantVelocity.y > 0 ? 1 : Aimbot.INSTANCE.predictionTicks.getRawState().intValue()); + double zVelocity = instantVelocity.z * Aimbot.INSTANCE.predictionTicks.getRawState().intValue(); + Vec3d currentVelocity = new Vec3d(xVelocity, yVelocity, zVelocity); + + Vec3d pathDirection = getPathfindingDirection(null, currentPos, playerPos, currentVelocity); + + if (pathDirection.lengthSquared() < 1.0E-7D) { + return target.getEyePos(); + } + + double distance = playerPos.distanceTo(currentPos); + double projectileSpeed = 20.0; + double timeToTarget = distance / projectileSpeed; + + int maxPredictionTicks = Aimbot.INSTANCE.predictionTicks.getRawState().intValue(); + double maxPredictionTime = maxPredictionTicks / 20.0; + timeToTarget = Math.min(timeToTarget, maxPredictionTime); + + Vec3d predictedBodyPos = currentPos.add(pathDirection.multiply(timeToTarget)); + + return predictedBodyPos.add(0, target.getEyeHeight(target.getPose()), 0); + } + + private static Vec3d getPathfindingDirection(ZombiesAgent.ZombieTarget zombie, Vec3d zombiePos, Vec3d playerPos, Vec3d currentVelocity) { Vec3d directPath = playerPos.subtract(zombiePos).normalize(); if (currentVelocity.lengthSquared() > 1.0E-7D) { @@ -133,7 +162,7 @@ private Vec3d getPathfindingDirection(ZombiesAgent.ZombieTarget zombie, Vec3d zo return directPath.multiply(estimateZombieSpeed(zombie) / 20.0); } - private double estimateZombieSpeed(ZombiesAgent.ZombieTarget zombie) { + private static double estimateZombieSpeed(ZombiesAgent.ZombieTarget zombie) { return switch (zombie.type) { case ZOMBIE, SKELETON, CREEPER, WITCH -> 5.0; // ~0.25 B/t * 20 t/s case BLAZE -> 8.0; diff --git a/src/main/java/dev/cigarette/config/Config.java b/src/main/java/dev/cigarette/config/Config.java index 7af5c2ef..ad617da6 100644 --- a/src/main/java/dev/cigarette/config/Config.java +++ b/src/main/java/dev/cigarette/config/Config.java @@ -8,6 +8,7 @@ import dev.cigarette.module.combat.AutoClicker; import dev.cigarette.module.combat.JumpReset; import dev.cigarette.module.combat.PerfectHit; +import dev.cigarette.module.combat.PlayerAimbot; import dev.cigarette.module.keybind.AddGlassBlock; import dev.cigarette.module.keybind.BreakBlock; import dev.cigarette.module.keybind.VClip; @@ -68,7 +69,7 @@ public void positionCategories() { public static Config construct() { Config cfg = new Config(); cfg.putModules("Bedwars", AutoBlockIn.INSTANCE, AutoTool.INSTANCE, Bridger.INSTANCE, DefenseViewer.INSTANCE, EntityESP.INSTANCE, FireballESP.INSTANCE); - cfg.putModules("Combat", AutoClicker.INSTANCE, JumpReset.INSTANCE, PerfectHit.INSTANCE); + cfg.putModules("Combat", AutoClicker.INSTANCE, JumpReset.INSTANCE, PerfectHit.INSTANCE, PlayerAimbot.INSTANCE); cfg.putModules("Keybind", AddGlassBlock.INSTANCE, BreakBlock.INSTANCE, VClip.INSTANCE); cfg.putModules("Murder Mystery", GoldESP.INSTANCE, PlayerESP.INSTANCE); cfg.putModules("Render", dev.cigarette.module.render.PlayerESP.INSTANCE, ProjectileESP.INSTANCE); diff --git a/src/main/java/dev/cigarette/gui/widget/SliderWidget.java b/src/main/java/dev/cigarette/gui/widget/SliderWidget.java index a0afa7e2..773e33ba 100644 --- a/src/main/java/dev/cigarette/gui/widget/SliderWidget.java +++ b/src/main/java/dev/cigarette/gui/widget/SliderWidget.java @@ -13,9 +13,9 @@ public class SliderWidget extends BaseWidget { private static final int SLIDER_PADDING = 4; private @Nullable Consumer sliderCallback = null; - private double maxState = 0; - private double minState = 0; - private int decimalPlaces = 0; + double maxState = 0; + double minState = 0; + int decimalPlaces = 0; private boolean dragging = false; public boolean disabled = false; @@ -121,4 +121,71 @@ protected void render(DrawContext context, boolean hovered, int mouseX, int mous context.drawVerticalLine(sliderXState, bottom - 7, bottom - 1, primaryColor); context.drawVerticalLine(sliderXState + 1, bottom - 6, bottom - 2, primaryColor); } + + public class TwoHandedSliderWidget extends SliderWidget { + private final SliderWidget secondSlider; + + public TwoHandedSliderWidget(int x, int y, int width, int height, String message, @Nullable String tooltip) { + super(x, y, width, height, message, tooltip); + secondSlider = new SliderWidget(x + width + 5, y, width, height, message + " 2", tooltip); + secondSlider.withBounds(this.minState, this.maxState, this.maxState); + secondSlider.withAccuracy(this.decimalPlaces); + secondSlider.sliderCallback = (value) -> { + if (value < this.getRawState()) { + this.setState(value); + } + }; + } + + public TwoHandedSliderWidget(int x, int y, int width, int height, String message) { + super(x, y, width, height, message); + secondSlider = new SliderWidget(x + width + 5, y, width, height, message + " 2"); + secondSlider.withBounds(this.minState, this.maxState, this.maxState); + secondSlider.withAccuracy(this.decimalPlaces); + secondSlider.sliderCallback = (value) -> { + if (value < this.getRawState()) { + this.setState(value); + } + }; + } + + public TwoHandedSliderWidget(String message, String tooltip) { + super(message, tooltip); + secondSlider = new SliderWidget(message + " 2", tooltip); + secondSlider.withBounds(this.minState, this.maxState, this.maxState); + secondSlider.withAccuracy(this.decimalPlaces); + secondSlider.sliderCallback = (value) -> { + if (value < this.getRawState()) { + this.setState(value); + } + }; + } + + public TwoHandedSliderWidget(String message) { + super(message); + secondSlider = new SliderWidget(message + " 2"); + secondSlider.withBounds(this.minState, this.maxState, this.maxState); + secondSlider.withAccuracy(this.decimalPlaces); + secondSlider.sliderCallback = (value) -> { + if (value < this.getRawState()) { + this.setState(value); + } + }; + } + + @Override + public void setState(double state) { + super.setState(state); + if (secondSlider.getRawState() < state) { + secondSlider.setState(state); + } + } + + @Override + protected void render(DrawContext context, boolean hovered, int mouseX, int mouseY, float deltaTicks, int left, int top, int right, int bottom) { + super.render(context, hovered, mouseX, mouseY, deltaTicks, left, top, right, bottom); + secondSlider.withXY(left + this.getWidth() + 5, top); + secondSlider.render(context, secondSlider.isMouseOver(mouseX, mouseY), mouseX, mouseY, deltaTicks, secondSlider.getX(), secondSlider.getY(), secondSlider.getX() + secondSlider.getWidth(), secondSlider.getY() + secondSlider.getHeight()); + } + } } diff --git a/src/main/java/dev/cigarette/lib/PlayerEntityL.java b/src/main/java/dev/cigarette/lib/PlayerEntityL.java index e4e8b8ff..a0ae42f5 100644 --- a/src/main/java/dev/cigarette/lib/PlayerEntityL.java +++ b/src/main/java/dev/cigarette/lib/PlayerEntityL.java @@ -1,7 +1,9 @@ package dev.cigarette.lib; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.BowItem; import net.minecraft.item.ItemStack; +import net.minecraft.registry.tag.ItemTags; import net.minecraft.util.math.Vec3d; import org.jetbrains.annotations.Nullable; @@ -23,9 +25,7 @@ public static void setRotationVector(PlayerEntity player, Vec3d vector) { player.setPitch(yawPitch[1]); } - public static float angleBetween(float yaw, float pitch, float yaw2, float pitch2) { - float yawDiff = Math.abs(((yaw2 - yaw + 180) % 360) - 180); - float pitchDiff = Math.abs(pitch2 - pitch); - return (float) Math.sqrt(yawDiff * yawDiff + pitchDiff * pitchDiff) % 360; + public static float getDistance(PlayerEntity player, PlayerEntity other) { + return (float) player.getPos().distanceTo(other.getPos()); } } diff --git a/src/main/java/dev/cigarette/lib/ServerL.java b/src/main/java/dev/cigarette/lib/ServerL.java new file mode 100644 index 00000000..922c539e --- /dev/null +++ b/src/main/java/dev/cigarette/lib/ServerL.java @@ -0,0 +1,76 @@ +package dev.cigarette.lib; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.component.ComponentType; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.AttributeModifiersComponent; +import net.minecraft.component.type.DyedColorComponent; +import net.minecraft.entity.attribute.EntityAttributes; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.recipe.ArmorDyeRecipe; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.Formatting; + +import java.util.Objects; + +public class ServerL { + public static boolean playerOnSameTeam(ServerPlayerEntity player, ServerPlayerEntity other) { + if (player.getScoreboardTeam() == null || other.getScoreboardTeam() == null) return false; + return player.getScoreboardTeam().isEqual(other.getScoreboardTeam()) || + getScoreboardColor(player) == getScoreboardColor(other) || + getArmorColor(player) == getArmorColor(other) || + getNameColor(player) == getNameColor(other); + } + + public static int getNameColor(ServerPlayerEntity player) { + return Objects.requireNonNull( + Objects.requireNonNull(player.getDisplayName()).getStyle().getColor() + ).getRgb(); + } + + public static int getScoreboardColor(ServerPlayerEntity player) { + if (player.getScoreboardTeam() == null) return -1; + try { + Formatting color = player.getScoreboardTeam().getColor(); + if (color == null) return -1; + assert color.getColorValue() != null; + return color.getColorValue(); + } catch (NullPointerException e) { + return -1; + } + } + + public static int getArmorColor(ServerPlayerEntity player) { + if (player.getInventory() == null) return -1; + + ItemStack boots = player.getInventory().getStack(36); + ItemStack leggings = player.getInventory().getStack(37); + ItemStack chestplate = player.getInventory().getStack(38); + ItemStack helmet = player.getInventory().getStack(39); + + DyedColorComponent b = getDyedColorComponent(boots); + DyedColorComponent l = getDyedColorComponent(leggings); +// DyedColorComponent c = getDyedColorComponent(chestplate); +// DyedColorComponent h = getDyedColorComponent(helmet); + + if (b != null && l != null) { + if (b.rgb() == l.rgb()) { + return b.rgb(); + } + } + + return -1; + } + + private static DyedColorComponent getDyedColorComponent(ItemStack stack) { + ComponentType type = DataComponentTypes.DYED_COLOR; + return stack.getItem().getComponents().get(type); + } + + public static ServerPlayerEntity getPlayer(ClientPlayerEntity player) { + assert MinecraftClient.getInstance().getServer() != null; + return MinecraftClient.getInstance().getServer().getPlayerManager().getPlayer(player.getUuid()); + } +} \ No newline at end of file diff --git a/src/main/java/dev/cigarette/lib/WeaponSelector.java b/src/main/java/dev/cigarette/lib/WeaponSelector.java index 30ca9b27..290f362a 100644 --- a/src/main/java/dev/cigarette/lib/WeaponSelector.java +++ b/src/main/java/dev/cigarette/lib/WeaponSelector.java @@ -6,6 +6,7 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.tooltip.TooltipType; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; import org.jetbrains.annotations.Nullable; @@ -160,6 +161,34 @@ public static WeaponStats selectBestWeapon(ClientPlayerEntity player, @Nullable return bestWeapon; } + /** + * Selects the best weapon for auto-shooting based on situation + */ + @Nullable + public static WeaponStats selectBestWeapon(ClientPlayerEntity player, @Nullable ServerPlayerEntity target) { + List weapons = analyzeWeapons(player); + if (weapons.isEmpty()) { + return null; + } + + // If no target, we can assume a default distance and no headshot possibility for scoring. + double distance = (target != null) ? PlayerEntityL.getDistance(player, target) : 10.0; // Default distance + + WeaponStats bestWeapon = null; + double bestScore = -1; + + for (WeaponStats weapon : weapons) { + double score = calculateWeaponScore(weapon, distance, true, player); + if (score > bestScore) { + bestScore = score; + bestWeapon = weapon; + } + } + return bestWeapon; + } + + + /** * Calculates weapon score based on situation */ @@ -215,6 +244,25 @@ public static boolean switchToBestWeapon(ClientPlayerEntity player, @Nullable Zo return true; } + /** + * Switches to the best weapon if it's not already selected + */ + public static boolean switchToBestWeapon(ClientPlayerEntity player, @Nullable ServerPlayerEntity target) { + WeaponStats bestWeapon = selectBestWeapon(player, target); + + if (bestWeapon == null) { + return false; + } + + int currentSlot = player.getInventory().getSelectedSlot(); + if (currentSlot == bestWeapon.slotIndex) { + return true; + } + + player.getInventory().setSelectedSlot(bestWeapon.slotIndex); + return true; + } + /** * Gets stats for currently held weapon */ diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java new file mode 100644 index 00000000..251b2d12 --- /dev/null +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -0,0 +1,205 @@ +package dev.cigarette.module.combat; + +import dev.cigarette.GameDetector; +import dev.cigarette.gui.widget.SliderWidget; +import dev.cigarette.gui.widget.ToggleWidget; +import dev.cigarette.lib.InputOverride; +import dev.cigarette.lib.ServerL; +import dev.cigarette.lib.WeaponSelector; +import dev.cigarette.lib.WorldL; +import dev.cigarette.mixin.ClientWorldAccessor; +import dev.cigarette.module.TickModule; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.AbstractClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.PendingUpdateManager; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.EntityPose; +import net.minecraft.entity.LivingEntity; +import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket; +import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket; +import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket; +import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import org.jetbrains.annotations.NotNull; + +public class PlayerAimbot extends TickModule { + public static final PlayerAimbot INSTANCE = new PlayerAimbot("combat.playerAimbot", "PlayerAimbot", "Automatically aims at players."); + +// private final ToggleWidget silentAim = new ToggleWidget("Silent Aim", "Doesn't snap your camera client-side.").withDefaultState(true); + private final ToggleWidget autoAttack = new ToggleWidget("Auto Attack", "Automatically hits players").withDefaultState(true); + private final ToggleWidget autoWeaponSwitch = new ToggleWidget("Auto Weapon Switch", "Automatically switch weapons").withDefaultState(true); + public final ToggleWidget predictiveAim = new ToggleWidget("Predictive Aim", "Predict player movement for better accuracy").withDefaultState(true); + public final SliderWidget predictionTicks = new SliderWidget("Prediction Ticks", "How many ticks ahead to predict player movement").withBounds(1, 10, 20).withAccuracy(0); + public final SliderWidget smoothAim = new SliderWidget("Smooth Aim", "How quickly to snap to target in ticks").withBounds(1, 5, 20).withAccuracy(0); + public final ToggleWidget wTap = new ToggleWidget("W-Tap", "Automatically sprint before attacking").withDefaultState(false); + + private KeyBinding rightClickKey = null; + + private boolean hasLastAim = false; + private float lastAimYaw = 0f; + private float lastAimPitch = 0f; + + private PlayerAimbot(String id, String name, String tooltip) { + super(ToggleWidget::module, id, name, tooltip); + this.setChildren(autoAttack, autoWeaponSwitch, predictiveAim, predictionTicks, smoothAim, wTap); + autoAttack.registerConfigKey(id + ".autoAttack"); + autoWeaponSwitch.registerConfigKey(id + ".autoWeaponSwitch"); + predictiveAim.registerConfigKey(id + ".predictiveAim"); + predictionTicks.registerConfigKey(id + ".predictionTicks"); + smoothAim.registerConfigKey(id + ".smoothAim"); + wTap.registerConfigKey(id + ".wTap"); + } + + @Override + protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { + if (rightClickKey == null) { + rightClickKey = KeyBinding.byId("key.use"); + return; + } + + if (rightClickKey.isPressed() || autoAttack.getRawState()) { + HitResult hitResult = client.crosshairTarget; + if (hitResult != null && hitResult.getType() == HitResult.Type.BLOCK) { + BlockHitResult blockResult = (BlockHitResult) hitResult; + BlockState lookingAt = world.getBlockState(blockResult.getBlockPos()); + if (lookingAt.isIn(BlockTags.BUTTONS) || lookingAt.isOf(Blocks.CHEST)) return; + } + + AbstractClientPlayerEntity bestTarget = WorldL.getRealPlayers().stream().sorted((p1, p2) -> { + Vec3d eyePos = player.getEyePos(); + Vec3d p1Pos = p1.getEyePos().subtract(eyePos); + Vec3d p2Pos = p2.getEyePos().subtract(eyePos); + double p1Dist = p1Pos.lengthSquared(); + double p2Dist = p2Pos.lengthSquared(); + + if (p1.isSneaking() && !p2.isSneaking()) p1Dist *= 0.8; + else if (!p1.isSneaking() && p2.isSneaking()) p2Dist *= 0.8; + + if (p1.getPose() == EntityPose.CROUCHING && p2.getPose() != EntityPose.CROUCHING) p1Dist *= 0.9; + else if (p1.getPose() != EntityPose.CROUCHING && p2.getPose() == EntityPose.CROUCHING) p2Dist *= 0.9; + + if (p1.isSprinting() && !p2.isSprinting()) p1Dist *= 1.1; + else if (!p1.isSprinting() && p2.isSprinting()) p2Dist *= 1.1; + if (p1.getVelocity().lengthSquared() > 0.01 && p2.getVelocity().lengthSquared() < 0.01) p1Dist *= 1.1; + else if (p1.getVelocity().lengthSquared() < 0.01 && p2.getVelocity().lengthSquared() > 0.01) p2Dist *= 1.1; + return Double.compare(p1Dist, p2Dist); + }).filter(p -> { + if (p == player) return false; + if (player.isTeammate(p) || ServerL.playerOnSameTeam(ServerL.getPlayer(player), ServerL.getPlayer((ClientPlayerEntity) p))) return false; + if (p.isDead() || p.getHealth() <= 0) return false; + if (p.isInvulnerable()) return false; + if (p.age < 20) return false; + if (p.distanceTo(player) > 6) return false; + return player.canSee(p); + }).findFirst().orElse(null); + + if (bestTarget == null) { + hasLastAim = false; + return; + } + + if (autoWeaponSwitch.getRawState()) { + WeaponSelector.switchToBestWeapon(player, (net.minecraft.server.network.ServerPlayerEntity) null); + } + + Vec3d predictedPos; + if (predictiveAim.getRawState()) { + Vec3d currentPos = bestTarget.getPos(); + Vec3d instantVelocity = currentPos.subtract(bestTarget.lastX, bestTarget.lastY, bestTarget.lastZ); + int ticks = predictionTicks.getRawState().intValue(); + double xVelocity = instantVelocity.x * ticks; + double yVelocity = instantVelocity.y > LivingEntity.GRAVITY ? 0 : instantVelocity.y * (instantVelocity.y > 0 ? 1 : ticks); + double zVelocity = instantVelocity.z * ticks; + Vec3d projected = currentPos.add(xVelocity, yVelocity, zVelocity); + predictedPos = projected.add(0, bestTarget.getEyeHeight(bestTarget.getPose()), 0); + } else { + predictedPos = bestTarget.getEyePos(); + } + + Vec3d vector = predictedPos.subtract(player.getEyePos()).normalize(); + + float targetYaw = (float) Math.toDegrees(Math.atan2(-vector.x, vector.z)); + float targetPitch = (float) Math.toDegrees(Math.asin(-vector.y)); + + int smoothTicks = smoothAim.getRawState().intValue(); + float sendYaw; + float sendPitch; + if (!hasLastAim) { + lastAimYaw = targetYaw; + lastAimPitch = targetPitch; + hasLastAim = true; + } + if (smoothTicks <= 1) { + sendYaw = targetYaw; + sendPitch = targetPitch; + } else { + float yawDiff = wrapDegrees(targetYaw - lastAimYaw); + float pitchDiff = targetPitch - lastAimPitch; + float stepYaw = yawDiff / smoothTicks; + float stepPitch = pitchDiff / smoothTicks; + sendYaw = lastAimYaw + stepYaw; + sendPitch = lastAimPitch + stepPitch; + } + lastAimYaw = sendYaw; + lastAimPitch = sendPitch; + + WeaponSelector.addCooldown(player.getInventory().getSelectedSlot()); + + boolean isGun = dev.cigarette.agent.ZombiesAgent.isGun(player.getMainHandStack()); + if (isGun) { + ClientWorldAccessor clientWorldAccessor = (ClientWorldAccessor) world; + try (PendingUpdateManager pendingUpdateManager = clientWorldAccessor.getPendingUpdateManager().incrementSequence()) { + int seq = pendingUpdateManager.getSequence(); + player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.MAIN_HAND, seq, sendYaw, sendPitch)); + } + } else { + if (wTap.getRawState()) { + doSprint(false); + doSprint(true); + } + + player.setYaw(sendYaw); + player.setPitch(sendPitch); + player.networkHandler.sendPacket(PlayerInteractEntityC2SPacket.attack(bestTarget, player.isSneaking())); + player.networkHandler.sendPacket(new HandSwingC2SPacket(Hand.MAIN_HAND)); + } + } else { + hasLastAim = false; + } + } + + private static float wrapDegrees(float degrees) { + return MathHelper.wrapDegrees(degrees); + } + + private static void doSprint(boolean sprint) { + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (player == null) return; + if (!player.isOnGround() || !InputOverride.forwardKey) return; + if (sprint) { + if (!player.isSprinting() && !player.isUsingItem() && !player.isSneaking()) { + player.setSprinting(true); + player.networkHandler.sendPacket(new ClientCommandC2SPacket(player, ClientCommandC2SPacket.Mode.START_SPRINTING)); + } + } else { + if (player.isSprinting()) { + player.setSprinting(false); + player.networkHandler.sendPacket(new ClientCommandC2SPacket(player, ClientCommandC2SPacket.Mode.STOP_SPRINTING)); + } + } + } + + @Override + public boolean inValidGame() { + return GameDetector.rootGame == GameDetector.ParentGame.BEDWARS; + } +} From a7f369ab736eee6c359ea01a932edd5264b80909 Mon Sep 17 00:00:00 2001 From: AH Date: Wed, 27 Aug 2025 00:06:08 -0400 Subject: [PATCH 02/23] fix: ensure on ground and movement state is checked properly prior to w-tap --- src/main/java/dev/cigarette/module/combat/PlayerAimbot.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index 251b2d12..e37488a4 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -184,7 +184,8 @@ private static float wrapDegrees(float degrees) { private static void doSprint(boolean sprint) { ClientPlayerEntity player = MinecraftClient.getInstance().player; if (player == null) return; - if (!player.isOnGround() || !InputOverride.forwardKey) return; + boolean forwardPressed = InputOverride.isActive ? InputOverride.forwardKey : KeyBinding.byId("key.forward").isPressed(); + if (!player.isOnGround() || !forwardPressed) return; if (sprint) { if (!player.isSprinting() && !player.isUsingItem() && !player.isSneaking()) { player.setSprinting(true); From 14ac3f32a24182bd022d4b9d54953d955d7cf320 Mon Sep 17 00:00:00 2001 From: AH Date: Wed, 27 Aug 2025 08:38:24 -0400 Subject: [PATCH 03/23] fix: fix crashing when joining server and add smooth jitter --- .../cigarette/gui/widget/SliderWidget.java | 75 +++++++++++++++++++ src/main/java/dev/cigarette/lib/ServerL.java | 67 +++++++++-------- .../dev/cigarette/lib/WeaponSelector.java | 23 ++++-- .../cigarette/module/combat/PlayerAimbot.java | 45 +++++++++-- 4 files changed, 170 insertions(+), 40 deletions(-) diff --git a/src/main/java/dev/cigarette/gui/widget/SliderWidget.java b/src/main/java/dev/cigarette/gui/widget/SliderWidget.java index 773e33ba..9abf97cc 100644 --- a/src/main/java/dev/cigarette/gui/widget/SliderWidget.java +++ b/src/main/java/dev/cigarette/gui/widget/SliderWidget.java @@ -188,4 +188,79 @@ protected void render(DrawContext context, boolean hovered, int mouseX, int mous secondSlider.render(context, secondSlider.isMouseOver(mouseX, mouseY), mouseX, mouseY, deltaTicks, secondSlider.getX(), secondSlider.getY(), secondSlider.getX() + secondSlider.getWidth(), secondSlider.getY() + secondSlider.getHeight()); } } + + // New static variant usable directly: primary slider = minimum, secondary = maximum. + public static class TwoHandedSlider extends SliderWidget { + private final SliderWidget maxSlider; + + public TwoHandedSlider(String message, String tooltip) { + super(message, tooltip); + maxSlider = new SliderWidget(message + " Max", tooltip); + syncBounds(); + linkCallbacks(); + } + + public TwoHandedSlider(String message) { + super(message); + maxSlider = new SliderWidget(message + " Max"); + syncBounds(); + linkCallbacks(); + } + + private void syncBounds() { + maxSlider.withBounds(this.minState, this.maxState, this.maxState); + maxSlider.withAccuracy(this.decimalPlaces); + } + + private void linkCallbacks() { + maxSlider.sliderCallback = (value) -> { + // Ensure max >= min + if (value < this.getRawState()) { + this.setState(value); + } + }; + } + + @Override + public SliderWidget withBounds(double min, double def, double max) { + super.withBounds(min, def, max); + syncBounds(); + // Ensure ordering + if (maxSlider.getRawState() < getRawState()) maxSlider.setState(getRawState()); + return this; + } + + @Override + public SliderWidget withAccuracy(int decimalPlaces) { + super.withAccuracy(decimalPlaces); + maxSlider.withAccuracy(decimalPlaces); + return this; + } + + @Override + public void setState(double state) { + super.setState(state); + if (maxSlider.getRawState() < state) { + maxSlider.setState(state); + } + } + + @Override + public void registerConfigKey(String key) { + // Store min and max separately + super.registerConfigKey(key + ".min"); + maxSlider.registerConfigKey(key + ".max"); + } + + public double getMinValue() { return this.getRawState(); } + public double getMaxValue() { return maxSlider.getRawState(); } + + @Override + protected void render(DrawContext context, boolean hovered, int mouseX, int mouseY, float deltaTicks, int left, int top, int right, int bottom) { + super.render(context, hovered, mouseX, mouseY, deltaTicks, left, top, right, bottom); + // Position max slider to the right with small gap + maxSlider.withXY(left + this.getWidth() + 5, top).withWH(this.getWidth(), this.getHeight()); + maxSlider.render(context, maxSlider.isMouseOver(mouseX, mouseY), mouseX, mouseY, deltaTicks, maxSlider.getX(), maxSlider.getY(), maxSlider.getX() + maxSlider.getWidth(), maxSlider.getY() + maxSlider.getHeight()); + } + } } diff --git a/src/main/java/dev/cigarette/lib/ServerL.java b/src/main/java/dev/cigarette/lib/ServerL.java index 922c539e..366c4656 100644 --- a/src/main/java/dev/cigarette/lib/ServerL.java +++ b/src/main/java/dev/cigarette/lib/ServerL.java @@ -4,73 +4,80 @@ import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.component.ComponentType; import net.minecraft.component.DataComponentTypes; -import net.minecraft.component.type.AttributeModifiersComponent; import net.minecraft.component.type.DyedColorComponent; -import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; -import net.minecraft.recipe.ArmorDyeRecipe; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Formatting; import java.util.Objects; public class ServerL { + // Generic variant usable on both client and server player entities. + public static boolean playerOnSameTeam(PlayerEntity player, PlayerEntity other) { + if (player == null || other == null) return false; + if (player.getScoreboardTeam() != null && other.getScoreboardTeam() != null && player.getScoreboardTeam().isEqual(other.getScoreboardTeam())) + return true; + return getScoreboardColor(player) != -1 && getScoreboardColor(player) == getScoreboardColor(other) || + getArmorColor(player) != -1 && getArmorColor(player) == getArmorColor(other) || + getNameColor(player) != -1 && getNameColor(player) == getNameColor(other); + } + + // Backwards-compatible server-specific method (delegates to generic). public static boolean playerOnSameTeam(ServerPlayerEntity player, ServerPlayerEntity other) { - if (player.getScoreboardTeam() == null || other.getScoreboardTeam() == null) return false; - return player.getScoreboardTeam().isEqual(other.getScoreboardTeam()) || - getScoreboardColor(player) == getScoreboardColor(other) || - getArmorColor(player) == getArmorColor(other) || - getNameColor(player) == getNameColor(other); + return playerOnSameTeam((PlayerEntity) player, (PlayerEntity) other); } - public static int getNameColor(ServerPlayerEntity player) { - return Objects.requireNonNull( - Objects.requireNonNull(player.getDisplayName()).getStyle().getColor() - ).getRgb(); + public static int getNameColor(PlayerEntity player) { + try { + if (player == null || player.getDisplayName() == null) return -1; + var style = player.getDisplayName().getStyle(); + if (style.getColor() == null) return -1; + return style.getColor().getRgb(); + } catch (Exception ignored) { + return -1; + } } - public static int getScoreboardColor(ServerPlayerEntity player) { - if (player.getScoreboardTeam() == null) return -1; + public static int getScoreboardColor(PlayerEntity player) { + if (player == null || player.getScoreboardTeam() == null) return -1; try { Formatting color = player.getScoreboardTeam().getColor(); - if (color == null) return -1; - assert color.getColorValue() != null; + if (color == null || color.getColorValue() == null) return -1; return color.getColorValue(); - } catch (NullPointerException e) { + } catch (Exception ignored) { return -1; } } - public static int getArmorColor(ServerPlayerEntity player) { - if (player.getInventory() == null) return -1; + public static int getArmorColor(PlayerEntity player) { + if (player == null || player.getInventory() == null) return -1; ItemStack boots = player.getInventory().getStack(36); ItemStack leggings = player.getInventory().getStack(37); - ItemStack chestplate = player.getInventory().getStack(38); - ItemStack helmet = player.getInventory().getStack(39); + // Chestplate / helmet currently unused; keep commented for potential future logic. + // ItemStack chestplate = player.getInventory().getStack(38); + // ItemStack helmet = player.getInventory().getStack(39); DyedColorComponent b = getDyedColorComponent(boots); DyedColorComponent l = getDyedColorComponent(leggings); -// DyedColorComponent c = getDyedColorComponent(chestplate); -// DyedColorComponent h = getDyedColorComponent(helmet); - if (b != null && l != null) { - if (b.rgb() == l.rgb()) { - return b.rgb(); - } + if (b != null && l != null && b.rgb() == l.rgb()) { + return b.rgb(); } - return -1; } private static DyedColorComponent getDyedColorComponent(ItemStack stack) { + if (stack == null) return null; ComponentType type = DataComponentTypes.DYED_COLOR; return stack.getItem().getComponents().get(type); } + // Returns null (instead of crashing) when running on a remote/multiplayer server. public static ServerPlayerEntity getPlayer(ClientPlayerEntity player) { - assert MinecraftClient.getInstance().getServer() != null; - return MinecraftClient.getInstance().getServer().getPlayerManager().getPlayer(player.getUuid()); + MinecraftClient mc = MinecraftClient.getInstance(); + if (mc == null || mc.getServer() == null || player == null) return null; // mc.getServer() null on multiplayer clients + return mc.getServer().getPlayerManager().getPlayer(player.getUuid()); } } \ No newline at end of file diff --git a/src/main/java/dev/cigarette/lib/WeaponSelector.java b/src/main/java/dev/cigarette/lib/WeaponSelector.java index 290f362a..c47ce021 100644 --- a/src/main/java/dev/cigarette/lib/WeaponSelector.java +++ b/src/main/java/dev/cigarette/lib/WeaponSelector.java @@ -165,20 +165,21 @@ public static WeaponStats selectBestWeapon(ClientPlayerEntity player, @Nullable * Selects the best weapon for auto-shooting based on situation */ @Nullable - public static WeaponStats selectBestWeapon(ClientPlayerEntity player, @Nullable ServerPlayerEntity target) { + public static WeaponStats selectBestWeapon(ClientPlayerEntity player) { List weapons = analyzeWeapons(player); if (weapons.isEmpty()) { return null; } // If no target, we can assume a default distance and no headshot possibility for scoring. - double distance = (target != null) ? PlayerEntityL.getDistance(player, target) : 10.0; // Default distance + // double distance = (target != null) ? PlayerEntityL.getDistance(player, target) : 10.0; // Default distance + double distance = 10.0; // Default distance WeaponStats bestWeapon = null; double bestScore = -1; for (WeaponStats weapon : weapons) { - double score = calculateWeaponScore(weapon, distance, true, player); + double score = calculateWeaponScore(weapon, distance, true); if (score > bestScore) { bestScore = score; bestWeapon = weapon; @@ -225,6 +226,18 @@ private static double calculateWeaponScore(WeaponStats weapon, double distance, return score; } + private static double calculateWeaponScore(WeaponStats weapon, double distance, boolean canHeadshot) { + // Basic score is the weapon's DPS + double score = weapon.DPS; + + // Headshot bonus for high-damage weapons. + if (canHeadshot && weapon.damage > 15) { + score *= 1.2; + } + + return score; + } + /** * Switches to the best weapon if it's not already selected */ @@ -247,8 +260,8 @@ public static boolean switchToBestWeapon(ClientPlayerEntity player, @Nullable Zo /** * Switches to the best weapon if it's not already selected */ - public static boolean switchToBestWeapon(ClientPlayerEntity player, @Nullable ServerPlayerEntity target) { - WeaponStats bestWeapon = selectBestWeapon(player, target); + public static boolean switchToBestWeapon(ClientPlayerEntity player) { + WeaponStats bestWeapon = selectBestWeapon(player); if (bestWeapon == null) { return false; diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index e37488a4..f88c33e6 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -41,22 +41,31 @@ public class PlayerAimbot extends TickModule { public final SliderWidget predictionTicks = new SliderWidget("Prediction Ticks", "How many ticks ahead to predict player movement").withBounds(1, 10, 20).withAccuracy(0); public final SliderWidget smoothAim = new SliderWidget("Smooth Aim", "How quickly to snap to target in ticks").withBounds(1, 5, 20).withAccuracy(0); public final ToggleWidget wTap = new ToggleWidget("W-Tap", "Automatically sprint before attacking").withDefaultState(false); + public final SliderWidget.TwoHandedSlider jitterAmount = new SliderWidget.TwoHandedSlider("Jitter", "Random jitter range (min/max) degrees").withBounds(0, 0, 4).withAccuracy(2); + public final SliderWidget jitterSpeed = new SliderWidget("Jitter Speed", "How fast jitter target changes (ticks)").withBounds(5, 10, 40).withAccuracy(0); private KeyBinding rightClickKey = null; private boolean hasLastAim = false; private float lastAimYaw = 0f; private float lastAimPitch = 0f; + private float currentJitterYaw = 0f; + private float currentJitterPitch = 0f; + private float targetJitterYaw = 0f; + private float targetJitterPitch = 0f; + private int jitterTickCounter = 0; private PlayerAimbot(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(autoAttack, autoWeaponSwitch, predictiveAim, predictionTicks, smoothAim, wTap); + this.setChildren(autoAttack, autoWeaponSwitch, predictiveAim, predictionTicks, smoothAim, jitterAmount, jitterSpeed, wTap); autoAttack.registerConfigKey(id + ".autoAttack"); autoWeaponSwitch.registerConfigKey(id + ".autoWeaponSwitch"); predictiveAim.registerConfigKey(id + ".predictiveAim"); predictionTicks.registerConfigKey(id + ".predictionTicks"); smoothAim.registerConfigKey(id + ".smoothAim"); wTap.registerConfigKey(id + ".wTap"); + jitterAmount.registerConfigKey(id + ".jitter"); + jitterSpeed.registerConfigKey(id + ".jitterSpeed"); } @Override @@ -94,7 +103,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, return Double.compare(p1Dist, p2Dist); }).filter(p -> { if (p == player) return false; - if (player.isTeammate(p) || ServerL.playerOnSameTeam(ServerL.getPlayer(player), ServerL.getPlayer((ClientPlayerEntity) p))) return false; + if (player.isTeammate(p) || ServerL.playerOnSameTeam(player, p)) return false; if (p.isDead() || p.getHealth() <= 0) return false; if (p.isInvulnerable()) return false; if (p.age < 20) return false; @@ -108,7 +117,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, } if (autoWeaponSwitch.getRawState()) { - WeaponSelector.switchToBestWeapon(player, (net.minecraft.server.network.ServerPlayerEntity) null); + WeaponSelector.switchToBestWeapon(player); } Vec3d predictedPos; @@ -127,8 +136,28 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, Vec3d vector = predictedPos.subtract(player.getEyePos()).normalize(); - float targetYaw = (float) Math.toDegrees(Math.atan2(-vector.x, vector.z)); - float targetPitch = (float) Math.toDegrees(Math.asin(-vector.y)); + float baseYaw = (float) Math.toDegrees(Math.atan2(-vector.x, vector.z)); + float basePitch = (float) Math.toDegrees(Math.asin(-vector.y)); + + double minJitter = jitterAmount.getMinValue(); + double maxJitter = jitterAmount.getMaxValue(); + double jitterRange = maxJitter - minJitter; + int jitterSpeedTicks = (int) jitterSpeed.getRawState().doubleValue(); + if (jitterRange > 0 && jitterSpeedTicks > 0) { + if (jitterTickCounter++ >= jitterSpeedTicks) { + jitterTickCounter = 0; + targetJitterYaw = (float) randomSigned(minJitter, maxJitter); + targetJitterPitch = (float) randomSigned(minJitter, maxJitter); + } + float lerpFactor = 1f / Math.max(1, jitterSpeedTicks); + currentJitterYaw += (targetJitterYaw - currentJitterYaw) * lerpFactor; + currentJitterPitch += (targetJitterPitch - currentJitterPitch) * lerpFactor; + } else { + currentJitterYaw = currentJitterPitch = targetJitterYaw = targetJitterPitch = 0f; + } + + float targetYaw = baseYaw + currentJitterYaw; + float targetPitch = basePitch + currentJitterPitch; int smoothTicks = smoothAim.getRawState().intValue(); float sendYaw; @@ -180,6 +209,12 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, private static float wrapDegrees(float degrees) { return MathHelper.wrapDegrees(degrees); } + + private double randomSigned(double min, double max) { + if (max <= 0) return 0; + double magnitude = min + (Math.random() * (max - min)); + return (Math.random() < 0.5 ? -1 : 1) * magnitude; + } private static void doSprint(boolean sprint) { ClientPlayerEntity player = MinecraftClient.getInstance().player; From 43063a165b1d3fb68aedb2a9ec0bc0f38630ecc3 Mon Sep 17 00:00:00 2001 From: AH Date: Wed, 27 Aug 2025 15:13:49 -0400 Subject: [PATCH 04/23] fix: Add bar providers for aimbot targets. Separate out Zombies logic from Player-specific logic. --- .../dev/cigarette/gui/hud/bar/BarDisplay.java | 1 + .../bar/providers/AimbotTargetProvider.java | 73 ++++++++++++++ .../hud/bar/providers/DefaultProviders.java | 2 +- .../cigarette/gui/widget/SliderWidget.java | 10 +- .../dev/cigarette/lib/WeaponSelector.java | 84 +++++++++++++++- .../cigarette/module/combat/PlayerAimbot.java | 97 ++++++++++++------- 6 files changed, 221 insertions(+), 46 deletions(-) create mode 100644 src/main/java/dev/cigarette/gui/hud/bar/providers/AimbotTargetProvider.java diff --git a/src/main/java/dev/cigarette/gui/hud/bar/BarDisplay.java b/src/main/java/dev/cigarette/gui/hud/bar/BarDisplay.java index e28fc053..8ba792d7 100644 --- a/src/main/java/dev/cigarette/gui/hud/bar/BarDisplay.java +++ b/src/main/java/dev/cigarette/gui/hud/bar/BarDisplay.java @@ -71,6 +71,7 @@ protected void renderWidget(DrawContext context, int mouseX, int mouseY, float d List widgets = new ArrayList<>(); Set seenUuids = new HashSet<>(); List> providerOrder = List.of( + dev.cigarette.gui.hud.bar.providers.AimbotTargetProvider.class, dev.cigarette.gui.hud.bar.providers.MurderMysteryProvider.class, dev.cigarette.gui.hud.bar.providers.BedwarsProvider.class, dev.cigarette.gui.hud.bar.providers.ZombiesProvider.class, diff --git a/src/main/java/dev/cigarette/gui/hud/bar/providers/AimbotTargetProvider.java b/src/main/java/dev/cigarette/gui/hud/bar/providers/AimbotTargetProvider.java new file mode 100644 index 00000000..d2c8ee61 --- /dev/null +++ b/src/main/java/dev/cigarette/gui/hud/bar/providers/AimbotTargetProvider.java @@ -0,0 +1,73 @@ +package dev.cigarette.gui.hud.bar.providers; + +import dev.cigarette.gui.CigaretteScreen; +import dev.cigarette.gui.hud.bar.BarDisplay; +import dev.cigarette.gui.hud.bar.api.BarWidget; +import dev.cigarette.gui.hud.bar.api.BarWidgetProvider; +import dev.cigarette.gui.hud.bar.widgets.EntityChipWidget; +import dev.cigarette.module.combat.PlayerAimbot; +import dev.cigarette.module.zombies.Aimbot; +import dev.cigarette.agent.ZombiesAgent; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.network.AbstractClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; + +import java.util.List; +import java.util.UUID; + +public class AimbotTargetProvider implements BarWidgetProvider { + @Override + public void collect(MinecraftClient mc, ClientWorld world, TextRenderer tr, List out) { + if (mc == null || world == null || mc.player == null) return; + + boolean playerAimbotRunning = false; + boolean zombiesAimbotRunning = false; + try { playerAimbotRunning = PlayerAimbot.INSTANCE.isRunning(); } catch (Throwable ignored) {} + try { zombiesAimbotRunning = Aimbot.INSTANCE.isRunning(); } catch (Throwable ignored) {} + + if (playerAimbotRunning) { + AbstractClientPlayerEntity target = PlayerAimbot.getBestTargetFor(mc.player); + if (target != null) { + String label = target.getName().getString(); + double sortKey = 0d; + try { + double dx = target.getX() - mc.player.getX(); + double dz = target.getZ() - mc.player.getZ(); + sortKey = BarDisplay.angle(mc.player.getYaw(), dx, dz); + label += " (" + Math.round(mc.player.distanceTo(target)) + "m)"; + } catch (Throwable ignored) {} + float hp = Math.max(0f, Math.min(1f, target.getHealth() / target.getMaxHealth())); + out.add(new EntityChipWidget.Progress("aimbot:player:" + target.getUuid(), target, label, sortKey, 0, + hp, CigaretteScreen.SECONDARY_COLOR, CigaretteScreen.BACKGROUND_COLOR)); + } + } + + if (zombiesAimbotRunning) { + ZombiesAgent.ZombieTarget zt = ZombiesAgent.getClosestZombie(); + if (zt != null && zt.uuid != null) { + Entity ent = findEntityByUuid(world, zt.uuid); + String label = zt.type.getName(); + double sortKey = 0d; + if (ent == null) { + label = label + " [?]"; + sortKey = 180.0; + } else { + try { + double dx = ent.getX() - mc.player.getX(); + double dz = ent.getZ() - mc.player.getZ(); + sortKey = BarDisplay.angle(mc.player.getYaw(), dx, dz); + label += " (" + Math.round(mc.player.distanceTo(ent)) + "m)"; + } catch (Throwable ignored) {} + } + out.add(new EntityChipWidget("aimbot:zombies:" + zt.uuid, ent, label, sortKey, zt.type.getColor())); + } + } + } + + private static Entity findEntityByUuid(ClientWorld world, UUID uuid) { + for (Entity e : world.getEntities()) if (uuid.equals(e.getUuid())) return e; + return null; + } +} diff --git a/src/main/java/dev/cigarette/gui/hud/bar/providers/DefaultProviders.java b/src/main/java/dev/cigarette/gui/hud/bar/providers/DefaultProviders.java index 34006bca..00f964b1 100644 --- a/src/main/java/dev/cigarette/gui/hud/bar/providers/DefaultProviders.java +++ b/src/main/java/dev/cigarette/gui/hud/bar/providers/DefaultProviders.java @@ -10,6 +10,6 @@ public static void registerAll() { BarWidgetRegistry.register(new ZombiesProvider()); BarWidgetRegistry.register(new BedwarsProvider()); BarWidgetRegistry.register(new NearbyPlayersProvider()); + BarWidgetRegistry.register(new AimbotTargetProvider()); } } - diff --git a/src/main/java/dev/cigarette/gui/widget/SliderWidget.java b/src/main/java/dev/cigarette/gui/widget/SliderWidget.java index 9abf97cc..2de61695 100644 --- a/src/main/java/dev/cigarette/gui/widget/SliderWidget.java +++ b/src/main/java/dev/cigarette/gui/widget/SliderWidget.java @@ -122,6 +122,7 @@ protected void render(DrawContext context, boolean hovered, int mouseX, int mous context.drawVerticalLine(sliderXState + 1, bottom - 6, bottom - 2, primaryColor); } + public class TwoHandedSliderWidget extends SliderWidget { private final SliderWidget secondSlider; @@ -189,7 +190,6 @@ protected void render(DrawContext context, boolean hovered, int mouseX, int mous } } - // New static variant usable directly: primary slider = minimum, secondary = maximum. public static class TwoHandedSlider extends SliderWidget { private final SliderWidget maxSlider; @@ -214,7 +214,6 @@ private void syncBounds() { private void linkCallbacks() { maxSlider.sliderCallback = (value) -> { - // Ensure max >= min if (value < this.getRawState()) { this.setState(value); } @@ -222,16 +221,15 @@ private void linkCallbacks() { } @Override - public SliderWidget withBounds(double min, double def, double max) { + public TwoHandedSlider withBounds(double min, double def, double max) { super.withBounds(min, def, max); syncBounds(); - // Ensure ordering if (maxSlider.getRawState() < getRawState()) maxSlider.setState(getRawState()); return this; } @Override - public SliderWidget withAccuracy(int decimalPlaces) { + public TwoHandedSlider withAccuracy(int decimalPlaces) { super.withAccuracy(decimalPlaces); maxSlider.withAccuracy(decimalPlaces); return this; @@ -247,7 +245,6 @@ public void setState(double state) { @Override public void registerConfigKey(String key) { - // Store min and max separately super.registerConfigKey(key + ".min"); maxSlider.registerConfigKey(key + ".max"); } @@ -258,7 +255,6 @@ public void registerConfigKey(String key) { @Override protected void render(DrawContext context, boolean hovered, int mouseX, int mouseY, float deltaTicks, int left, int top, int right, int bottom) { super.render(context, hovered, mouseX, mouseY, deltaTicks, left, top, right, bottom); - // Position max slider to the right with small gap maxSlider.withXY(left + this.getWidth() + 5, top).withWH(this.getWidth(), this.getHeight()); maxSlider.render(context, maxSlider.isMouseOver(mouseX, mouseY), mouseX, mouseY, deltaTicks, maxSlider.getX(), maxSlider.getY(), maxSlider.getX() + maxSlider.getWidth(), maxSlider.getY() + maxSlider.getHeight()); } diff --git a/src/main/java/dev/cigarette/lib/WeaponSelector.java b/src/main/java/dev/cigarette/lib/WeaponSelector.java index c47ce021..d5b8bc45 100644 --- a/src/main/java/dev/cigarette/lib/WeaponSelector.java +++ b/src/main/java/dev/cigarette/lib/WeaponSelector.java @@ -3,8 +3,7 @@ import dev.cigarette.agent.ZombiesAgent; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; +import net.minecraft.item.*; import net.minecraft.item.tooltip.TooltipType; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; @@ -309,4 +308,83 @@ public static void addCooldown(int slotIndex) { } } } -} \ No newline at end of file + + // Generic helper to detect ranged weapons in vanilla PvP contexts (non-Zombies) + public static boolean isRangedWeapon(ItemStack stack) { + if (stack == null || stack.isEmpty()) return false; + Item item = stack.getItem(); + return (item instanceof BowItem) + || (item instanceof CrossbowItem) + || (item instanceof TridentItem) + || stack.isOf(Items.SNOWBALL) + || stack.isOf(Items.EGG) + || stack.isOf(Items.ENDER_PEARL); + } + + /** + * Switch to the best PvP weapon for players. For close range, prefer melee (best sword/axe in hotbar). + * For long range, prefer bow/crossbow if present. + * Returns true if a switch was made or the best weapon is already selected. + */ + public static boolean switchToBestPvPWeapon(ClientPlayerEntity player, double distanceToTarget) { + if (player == null) return false; + int current = player.getInventory().getSelectedSlot(); + int bestSlot; + if (distanceToTarget > 7.5) { + bestSlot = findBestRangedSlot(player); + if (bestSlot == -1) bestSlot = findBestMeleeSlot(player); + } else { + bestSlot = findBestMeleeSlot(player); + if (bestSlot == -1) bestSlot = findBestRangedSlot(player); + } + if (bestSlot == -1) return false; + if (bestSlot == current) return true; + player.getInventory().setSelectedSlot(bestSlot); + return true; + } + + private static int findBestRangedSlot(ClientPlayerEntity player) { + int best = -1; + for (int i = 0; i < 9; i++) { + ItemStack s = player.getInventory().getStack(i); + if (isRangedWeapon(s)) { + best = i; + // Prefer crossbow over bow if found later; for simplicity, last wins + } + } + return best; + } + + private static int findBestMeleeSlot(ClientPlayerEntity player) { + int best = -1; + int bestScore = Integer.MIN_VALUE; + for (int i = 0; i < 9; i++) { + ItemStack s = player.getInventory().getStack(i); + int score = meleeScore(s); + if (score > bestScore) { + bestScore = score; + best = i; + } + } + return (bestScore > Integer.MIN_VALUE) ? best : -1; + } + + private static int meleeScore(ItemStack s) { + if (s == null || s.isEmpty()) return Integer.MIN_VALUE; + Item item = s.getItem(); + if (s.isOf(Items.NETHERITE_SWORD)) return 90; + if (s.isOf(Items.DIAMOND_SWORD)) return 80; + if (s.isOf(Items.IRON_SWORD)) return 70; + if (s.isOf(Items.STONE_SWORD)) return 60; + if (s.isOf(Items.GOLDEN_SWORD)) return 55; + if (s.isOf(Items.WOODEN_SWORD)) return 50; + if (s.isOf(Items.NETHERITE_AXE)) return 75; + if (s.isOf(Items.DIAMOND_AXE)) return 68; + if (s.isOf(Items.IRON_AXE)) return 62; + if (s.isOf(Items.STONE_AXE)) return 56; + if (s.isOf(Items.GOLDEN_AXE)) return 50; + if (s.isOf(Items.WOODEN_AXE)) return 44; + return Integer.MIN_VALUE; + } +} + diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index f88c33e6..c293ec95 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -19,6 +19,7 @@ import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.EntityPose; import net.minecraft.entity.LivingEntity; +import net.minecraft.item.*; import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket; @@ -43,6 +44,7 @@ public class PlayerAimbot extends TickModule { public final ToggleWidget wTap = new ToggleWidget("W-Tap", "Automatically sprint before attacking").withDefaultState(false); public final SliderWidget.TwoHandedSlider jitterAmount = new SliderWidget.TwoHandedSlider("Jitter", "Random jitter range (min/max) degrees").withBounds(0, 0, 4).withAccuracy(2); public final SliderWidget jitterSpeed = new SliderWidget("Jitter Speed", "How fast jitter target changes (ticks)").withBounds(5, 10, 40).withAccuracy(0); + private final ToggleWidget blockHit = new ToggleWidget("Block-Hit", "Briefly block just before attacking (if shield available)").withDefaultState(false); private KeyBinding rightClickKey = null; @@ -57,7 +59,7 @@ public class PlayerAimbot extends TickModule { private PlayerAimbot(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(autoAttack, autoWeaponSwitch, predictiveAim, predictionTicks, smoothAim, jitterAmount, jitterSpeed, wTap); + this.setChildren(autoAttack, autoWeaponSwitch, predictiveAim, predictionTicks, smoothAim, jitterAmount, jitterSpeed, wTap, blockHit); autoAttack.registerConfigKey(id + ".autoAttack"); autoWeaponSwitch.registerConfigKey(id + ".autoWeaponSwitch"); predictiveAim.registerConfigKey(id + ".predictiveAim"); @@ -66,6 +68,7 @@ private PlayerAimbot(String id, String name, String tooltip) { wTap.registerConfigKey(id + ".wTap"); jitterAmount.registerConfigKey(id + ".jitter"); jitterSpeed.registerConfigKey(id + ".jitterSpeed"); + blockHit.registerConfigKey(id + ".blockHit"); } @Override @@ -83,33 +86,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, if (lookingAt.isIn(BlockTags.BUTTONS) || lookingAt.isOf(Blocks.CHEST)) return; } - AbstractClientPlayerEntity bestTarget = WorldL.getRealPlayers().stream().sorted((p1, p2) -> { - Vec3d eyePos = player.getEyePos(); - Vec3d p1Pos = p1.getEyePos().subtract(eyePos); - Vec3d p2Pos = p2.getEyePos().subtract(eyePos); - double p1Dist = p1Pos.lengthSquared(); - double p2Dist = p2Pos.lengthSquared(); - - if (p1.isSneaking() && !p2.isSneaking()) p1Dist *= 0.8; - else if (!p1.isSneaking() && p2.isSneaking()) p2Dist *= 0.8; - - if (p1.getPose() == EntityPose.CROUCHING && p2.getPose() != EntityPose.CROUCHING) p1Dist *= 0.9; - else if (p1.getPose() != EntityPose.CROUCHING && p2.getPose() == EntityPose.CROUCHING) p2Dist *= 0.9; - - if (p1.isSprinting() && !p2.isSprinting()) p1Dist *= 1.1; - else if (!p1.isSprinting() && p2.isSprinting()) p2Dist *= 1.1; - if (p1.getVelocity().lengthSquared() > 0.01 && p2.getVelocity().lengthSquared() < 0.01) p1Dist *= 1.1; - else if (p1.getVelocity().lengthSquared() < 0.01 && p2.getVelocity().lengthSquared() > 0.01) p2Dist *= 1.1; - return Double.compare(p1Dist, p2Dist); - }).filter(p -> { - if (p == player) return false; - if (player.isTeammate(p) || ServerL.playerOnSameTeam(player, p)) return false; - if (p.isDead() || p.getHealth() <= 0) return false; - if (p.isInvulnerable()) return false; - if (p.age < 20) return false; - if (p.distanceTo(player) > 6) return false; - return player.canSee(p); - }).findFirst().orElse(null); + AbstractClientPlayerEntity bestTarget = getBestTargetFor(player); if (bestTarget == null) { hasLastAim = false; @@ -117,7 +94,8 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, } if (autoWeaponSwitch.getRawState()) { - WeaponSelector.switchToBestWeapon(player); + double dist = player.distanceTo(bestTarget); + WeaponSelector.switchToBestPvPWeapon(player, dist); } Vec3d predictedPos; @@ -181,10 +159,8 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, lastAimYaw = sendYaw; lastAimPitch = sendPitch; - WeaponSelector.addCooldown(player.getInventory().getSelectedSlot()); - - boolean isGun = dev.cigarette.agent.ZombiesAgent.isGun(player.getMainHandStack()); - if (isGun) { + boolean isRanged = isRangedStack(player.getMainHandStack()); + if (isRanged) { ClientWorldAccessor clientWorldAccessor = (ClientWorldAccessor) world; try (PendingUpdateManager pendingUpdateManager = clientWorldAccessor.getPendingUpdateManager().incrementSequence()) { int seq = pendingUpdateManager.getSequence(); @@ -198,6 +174,15 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, player.setYaw(sendYaw); player.setPitch(sendPitch); + + if (blockHit.getRawState() && player.getOffHandStack() != null && player.getOffHandStack().isOf(Items.SHIELD)) { + ClientWorldAccessor clientWorldAccessor = (ClientWorldAccessor) world; + try (PendingUpdateManager pendingUpdateManager = clientWorldAccessor.getPendingUpdateManager().incrementSequence()) { + int seq = pendingUpdateManager.getSequence(); + player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.OFF_HAND, seq, sendYaw, sendPitch)); + } + } + player.networkHandler.sendPacket(PlayerInteractEntityC2SPacket.attack(bestTarget, player.isSneaking())); player.networkHandler.sendPacket(new HandSwingC2SPacket(Hand.MAIN_HAND)); } @@ -206,6 +191,47 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, } } + private static boolean isRangedStack(ItemStack stack) { + if (stack == null || stack.isEmpty()) return false; + Item item = stack.getItem(); + return (item instanceof BowItem) + || (item instanceof CrossbowItem) + || (item instanceof TridentItem) + || stack.isOf(Items.SNOWBALL) + || stack.isOf(Items.EGG) + || stack.isOf(Items.ENDER_PEARL); + } + + public static AbstractClientPlayerEntity getBestTargetFor(ClientPlayerEntity player) { + return WorldL.getRealPlayers().stream().sorted((p1, p2) -> { + Vec3d eyePos = player.getEyePos(); + Vec3d p1Pos = p1.getEyePos().subtract(eyePos); + Vec3d p2Pos = p2.getEyePos().subtract(eyePos); + double p1Dist = p1Pos.lengthSquared(); + double p2Dist = p2Pos.lengthSquared(); + + if (p1.isSneaking() && !p2.isSneaking()) p1Dist *= 0.8; + else if (!p1.isSneaking() && p2.isSneaking()) p2Dist *= 0.8; + + if (p1.getPose() == EntityPose.CROUCHING && p2.getPose() != EntityPose.CROUCHING) p1Dist *= 0.9; + else if (p1.getPose() != EntityPose.CROUCHING && p2.getPose() == EntityPose.CROUCHING) p2Dist *= 0.9; + + if (p1.isSprinting() && !p2.isSprinting()) p1Dist *= 1.1; + else if (!p1.isSprinting() && p2.isSprinting()) p2Dist *= 1.1; + if (p1.getVelocity().lengthSquared() > 0.01 && p2.getVelocity().lengthSquared() < 0.01) p1Dist *= 1.1; + else if (p1.getVelocity().lengthSquared() < 0.01 && p2.getVelocity().lengthSquared() > 0.01) p2Dist *= 1.1; + return Double.compare(p1Dist, p2Dist); + }).filter(p -> { + if (p == player) return false; + if (player.isTeammate(p) || ServerL.playerOnSameTeam(player, p)) return false; + if (p.isDead() || p.getHealth() <= 0) return false; + if (p.isInvulnerable()) return false; + if (p.age < 20) return false; + if (p.distanceTo(player) > 6) return false; + return player.canSee(p); + }).findFirst().orElse(null); + } + private static float wrapDegrees(float degrees) { return MathHelper.wrapDegrees(degrees); } @@ -219,7 +245,8 @@ private double randomSigned(double min, double max) { private static void doSprint(boolean sprint) { ClientPlayerEntity player = MinecraftClient.getInstance().player; if (player == null) return; - boolean forwardPressed = InputOverride.isActive ? InputOverride.forwardKey : KeyBinding.byId("key.forward").isPressed(); + KeyBinding forwardKey = KeyBinding.byId("key.forward"); + boolean forwardPressed = InputOverride.isActive ? InputOverride.forwardKey : (forwardKey != null && forwardKey.isPressed()); if (!player.isOnGround() || !forwardPressed) return; if (sprint) { if (!player.isSprinting() && !player.isUsingItem() && !player.isSneaking()) { @@ -236,6 +263,6 @@ private static void doSprint(boolean sprint) { @Override public boolean inValidGame() { - return GameDetector.rootGame == GameDetector.ParentGame.BEDWARS; + return GameDetector.rootGame != GameDetector.ParentGame.ZOMBIES; } } From 22b03a061d456ef3665c50bee2f7b94b06cd35f2 Mon Sep 17 00:00:00 2001 From: AH Date: Wed, 27 Aug 2025 20:54:16 -0400 Subject: [PATCH 05/23] fix: temporarily remove blockhit and prevent execution in menus to reduce flagging, ensure attacks are always within hitboxes. --- .../cigarette/module/combat/PlayerAimbot.java | 294 ++++++++++++------ src/main/resources/cigarette.accesswidener | 19 ++ src/main/resources/fabric.mod.json | 3 +- 3 files changed, 219 insertions(+), 97 deletions(-) create mode 100644 src/main/resources/cigarette.accesswidener diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index c293ec95..bde68ad6 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -7,68 +7,92 @@ import dev.cigarette.lib.ServerL; import dev.cigarette.lib.WeaponSelector; import dev.cigarette.lib.WorldL; -import dev.cigarette.mixin.ClientWorldAccessor; import dev.cigarette.module.TickModule; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.AbstractClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.network.PendingUpdateManager; import net.minecraft.client.option.KeyBinding; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.EntityPose; import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.ZombieVillagerEntity; +import net.minecraft.entity.passive.VillagerEntity; +import net.minecraft.entity.passive.WanderingTraderEntity; import net.minecraft.item.*; -import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket; -import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket; import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket; +import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket; import net.minecraft.registry.tag.BlockTags; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Box; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + public class PlayerAimbot extends TickModule { public static final PlayerAimbot INSTANCE = new PlayerAimbot("combat.playerAimbot", "PlayerAimbot", "Automatically aims at players."); // private final ToggleWidget silentAim = new ToggleWidget("Silent Aim", "Doesn't snap your camera client-side.").withDefaultState(true); private final ToggleWidget autoAttack = new ToggleWidget("Auto Attack", "Automatically hits players").withDefaultState(true); private final ToggleWidget autoWeaponSwitch = new ToggleWidget("Auto Weapon Switch", "Automatically switch weapons").withDefaultState(true); - public final ToggleWidget predictiveAim = new ToggleWidget("Predictive Aim", "Predict player movement for better accuracy").withDefaultState(true); - public final SliderWidget predictionTicks = new SliderWidget("Prediction Ticks", "How many ticks ahead to predict player movement").withBounds(1, 10, 20).withAccuracy(0); public final SliderWidget smoothAim = new SliderWidget("Smooth Aim", "How quickly to snap to target in ticks").withBounds(1, 5, 20).withAccuracy(0); public final ToggleWidget wTap = new ToggleWidget("W-Tap", "Automatically sprint before attacking").withDefaultState(false); public final SliderWidget.TwoHandedSlider jitterAmount = new SliderWidget.TwoHandedSlider("Jitter", "Random jitter range (min/max) degrees").withBounds(0, 0, 4).withAccuracy(2); public final SliderWidget jitterSpeed = new SliderWidget("Jitter Speed", "How fast jitter target changes (ticks)").withBounds(5, 10, 40).withAccuracy(0); - private final ToggleWidget blockHit = new ToggleWidget("Block-Hit", "Briefly block just before attacking (if shield available)").withDefaultState(false); +// private final ToggleWidget blockHit = new ToggleWidget("Block-Hit", "Briefly block just before attacking").withDefaultState(false); + private final ToggleWidget testMode = new ToggleWidget("Test Mode", "Allows targeting villagers regardless of team").withDefaultState(false); + public final SliderWidget attackCps = new SliderWidget("Attack CPS", "Clicks per second for auto attack").withBounds(1, 8, 15).withAccuracy(0); private KeyBinding rightClickKey = null; private boolean hasLastAim = false; private float lastAimYaw = 0f; private float lastAimPitch = 0f; - private float currentJitterYaw = 0f; - private float currentJitterPitch = 0f; - private float targetJitterYaw = 0f; - private float targetJitterPitch = 0f; - private int jitterTickCounter = 0; + + private long nextAttackAtMs = 0L; + private static final double ATTACK_VARIANCE = 0.15; + private static final double JITTER_CAP_DEGREES = 2.0; private PlayerAimbot(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(autoAttack, autoWeaponSwitch, predictiveAim, predictionTicks, smoothAim, jitterAmount, jitterSpeed, wTap, blockHit); + this.setChildren(autoAttack, autoWeaponSwitch, smoothAim, jitterAmount, jitterSpeed, wTap, testMode, attackCps); autoAttack.registerConfigKey(id + ".autoAttack"); autoWeaponSwitch.registerConfigKey(id + ".autoWeaponSwitch"); - predictiveAim.registerConfigKey(id + ".predictiveAim"); - predictionTicks.registerConfigKey(id + ".predictionTicks"); smoothAim.registerConfigKey(id + ".smoothAim"); wTap.registerConfigKey(id + ".wTap"); jitterAmount.registerConfigKey(id + ".jitter"); jitterSpeed.registerConfigKey(id + ".jitterSpeed"); - blockHit.registerConfigKey(id + ".blockHit"); +// blockHit.registerConfigKey(id + ".blockHit"); + testMode.registerConfigKey(id + ".testMode"); + attackCps.registerConfigKey(id + ".attackCps"); + } + + private static Vec3d getClosestBodyPos(LivingEntity from, LivingEntity to) { + Vec3d eyePos = from.getEyePos(); + Vec3d toPos = to.getPos(); + Vec3d toEyePos = to.getEyePos(); + Vec3d toFeetPos = new Vec3d(toPos.x, to.getY(), toPos.z); + Vec3d toMidPos = new Vec3d(toPos.x, to.getY() + to.getStandingEyeHeight() / 2, toPos.z); + Vec3d toHeadPos = new Vec3d(toPos.x, toEyePos.y, toPos.z); + Vec3d[] candidates = new Vec3d[] {toFeetPos, toMidPos, toHeadPos}; + Vec3d bestPos = toMidPos; + double bestDist = eyePos.distanceTo(bestPos); + for (Vec3d candidate : candidates) { + double dist = eyePos.distanceTo(candidate); + if (dist < bestDist) { + bestDist = dist; + bestPos = candidate; + } + } + return bestPos; } @Override @@ -78,7 +102,10 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, return; } - if (rightClickKey.isPressed() || autoAttack.getRawState()) { + if (MinecraftClient.getInstance().currentScreen != null) return; + + boolean active = rightClickKey.isPressed() || autoAttack.getRawState(); + if (active) { HitResult hitResult = client.crosshairTarget; if (hitResult != null && hitResult.getType() == HitResult.Type.BLOCK) { BlockHitResult blockResult = (BlockHitResult) hitResult; @@ -86,7 +113,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, if (lookingAt.isIn(BlockTags.BUTTONS) || lookingAt.isOf(Blocks.CHEST)) return; } - AbstractClientPlayerEntity bestTarget = getBestTargetFor(player); + LivingEntity bestTarget = testMode.getRawState() ? getBestEntityTargetFor(player) : getBestTargetFor(player); if (bestTarget == null) { hasLastAim = false; @@ -98,108 +125,126 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, WeaponSelector.switchToBestPvPWeapon(player, dist); } - Vec3d predictedPos; - if (predictiveAim.getRawState()) { - Vec3d currentPos = bestTarget.getPos(); - Vec3d instantVelocity = currentPos.subtract(bestTarget.lastX, bestTarget.lastY, bestTarget.lastZ); - int ticks = predictionTicks.getRawState().intValue(); - double xVelocity = instantVelocity.x * ticks; - double yVelocity = instantVelocity.y > LivingEntity.GRAVITY ? 0 : instantVelocity.y * (instantVelocity.y > 0 ? 1 : ticks); - double zVelocity = instantVelocity.z * ticks; - Vec3d projected = currentPos.add(xVelocity, yVelocity, zVelocity); - predictedPos = projected.add(0, bestTarget.getEyeHeight(bestTarget.getPose()), 0); - } else { - predictedPos = bestTarget.getEyePos(); - } + boolean isRanged = isRangedStack(player.getMainHandStack()); + boolean shouldAttemptMelee = autoAttack.getRawState() && !isRanged; + long now = System.currentTimeMillis(); + boolean attackNow = shouldAttemptMelee && now >= nextAttackAtMs; + if (attackNow) scheduleNextAttack(now); - Vec3d vector = predictedPos.subtract(player.getEyePos()).normalize(); + Vec3d aimPoint = getAimPointInsideHitbox(player, bestTarget, attackNow); + double reach = getMeleeReach(player); + boolean inReach = player.getEyePos().squaredDistanceTo(aimPoint) <= (reach * reach) + 1e-6; - float baseYaw = (float) Math.toDegrees(Math.atan2(-vector.x, vector.z)); - float basePitch = (float) Math.toDegrees(Math.asin(-vector.y)); - - double minJitter = jitterAmount.getMinValue(); - double maxJitter = jitterAmount.getMaxValue(); - double jitterRange = maxJitter - minJitter; - int jitterSpeedTicks = (int) jitterSpeed.getRawState().doubleValue(); - if (jitterRange > 0 && jitterSpeedTicks > 0) { - if (jitterTickCounter++ >= jitterSpeedTicks) { - jitterTickCounter = 0; - targetJitterYaw = (float) randomSigned(minJitter, maxJitter); - targetJitterPitch = (float) randomSigned(minJitter, maxJitter); - } - float lerpFactor = 1f / Math.max(1, jitterSpeedTicks); - currentJitterYaw += (targetJitterYaw - currentJitterYaw) * lerpFactor; - currentJitterPitch += (targetJitterPitch - currentJitterPitch) * lerpFactor; - } else { - currentJitterYaw = currentJitterPitch = targetJitterYaw = targetJitterPitch = 0f; - } - - float targetYaw = baseYaw + currentJitterYaw; - float targetPitch = basePitch + currentJitterPitch; + Vec3d vector = aimPoint.subtract(player.getEyePos()).normalize(); + float targetYaw = (float) Math.toDegrees(Math.atan2(-vector.x, vector.z)); + float targetPitch = (float) Math.toDegrees(Math.asin(-vector.y)); int smoothTicks = smoothAim.getRawState().intValue(); float sendYaw; float sendPitch; - if (!hasLastAim) { - lastAimYaw = targetYaw; - lastAimPitch = targetPitch; - hasLastAim = true; - } - if (smoothTicks <= 1) { + if (attackNow) { sendYaw = targetYaw; sendPitch = targetPitch; + hasLastAim = true; } else { - float yawDiff = wrapDegrees(targetYaw - lastAimYaw); - float pitchDiff = targetPitch - lastAimPitch; - float stepYaw = yawDiff / smoothTicks; - float stepPitch = pitchDiff / smoothTicks; - sendYaw = lastAimYaw + stepYaw; - sendPitch = lastAimPitch + stepPitch; + if (!hasLastAim) { + lastAimYaw = targetYaw; + lastAimPitch = targetPitch; + hasLastAim = true; + } + if (smoothTicks <= 1) { + sendYaw = targetYaw; + sendPitch = targetPitch; + } else { + float yawDiff = wrapDegrees(targetYaw - lastAimYaw); + float pitchDiff = targetPitch - lastAimPitch; + float stepYaw = yawDiff / smoothTicks; + float stepPitch = pitchDiff / smoothTicks; + sendYaw = lastAimYaw + stepYaw; + sendPitch = lastAimPitch + stepPitch; + } } lastAimYaw = sendYaw; lastAimPitch = sendPitch; - boolean isRanged = isRangedStack(player.getMainHandStack()); if (isRanged) { - ClientWorldAccessor clientWorldAccessor = (ClientWorldAccessor) world; - try (PendingUpdateManager pendingUpdateManager = clientWorldAccessor.getPendingUpdateManager().incrementSequence()) { - int seq = pendingUpdateManager.getSequence(); - player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.MAIN_HAND, seq, sendYaw, sendPitch)); - } - } else { - if (wTap.getRawState()) { - doSprint(false); - doSprint(true); - } - player.setYaw(sendYaw); player.setPitch(sendPitch); - - if (blockHit.getRawState() && player.getOffHandStack() != null && player.getOffHandStack().isOf(Items.SHIELD)) { - ClientWorldAccessor clientWorldAccessor = (ClientWorldAccessor) world; - try (PendingUpdateManager pendingUpdateManager = clientWorldAccessor.getPendingUpdateManager().incrementSequence()) { - int seq = pendingUpdateManager.getSequence(); - player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.OFF_HAND, seq, sendYaw, sendPitch)); + player.swingHand(Hand.MAIN_HAND); + } else { + if (attackNow && inReach) { + if (wTap.getRawState()) { + doSprint(false); + doSprint(true); } - } + player.setYaw(sendYaw); + player.setPitch(sendPitch); - player.networkHandler.sendPacket(PlayerInteractEntityC2SPacket.attack(bestTarget, player.isSneaking())); - player.networkHandler.sendPacket(new HandSwingC2SPacket(Hand.MAIN_HAND)); + player.swingHand(Hand.MAIN_HAND); + player.networkHandler.sendPacket(PlayerInteractEntityC2SPacket.attack(bestTarget, player.isSneaking())); + } else { + player.setYaw(sendYaw); + player.setPitch(sendPitch); + } } } else { hasLastAim = false; } } + private Vec3d getAimPointInsideHitbox(ClientPlayerEntity player, LivingEntity target, boolean attackNow) { + Box box = target.getBoundingBox(); + Vec3d eye = player.getEyePos(); + double cx = (box.minX + box.maxX) * 0.5; double cy = (box.minY + box.maxY) * 0.5; double cz = (box.minZ + box.maxZ) * 0.5; + double px = MathHelper.clamp(eye.x, box.minX, box.maxX); + double py = MathHelper.clamp(eye.y, box.minY, box.maxY); + double pz = MathHelper.clamp(eye.z, box.minZ, box.maxZ); + Vec3d closest = new Vec3d(px, py, pz); + Vec3d dirToCenter = new Vec3d(Math.copySign(1e-3, cx - px), Math.copySign(1e-3, cy - py), Math.copySign(1e-3, cz - pz)); + Vec3d basePoint = clampToBox(closest.add(dirToCenter), box); + + if (!attackNow) return basePoint; + + double halfX = (box.maxX - box.minX) * 0.5; + double halfY = (box.maxY - box.minY) * 0.5; + double halfZ = (box.maxZ - box.minZ) * 0.5; + double maxFrac = 0.3; + double jMin = Math.max(0.0, Math.min(jitterAmount.getMinValue(), JITTER_CAP_DEGREES)); + double jMax = Math.max(jMin, Math.min(jitterAmount.getMaxValue(), JITTER_CAP_DEGREES)); + double jitterScale = jMax <= 0 ? 0 : Math.min(1.0, jMax / JITTER_CAP_DEGREES); + double fx = maxFrac * jitterScale; + double fy = (maxFrac * 0.6) * jitterScale; + double fz = maxFrac * jitterScale; + + double ox = randomSigned(0, halfX * fx); + double oy = randomSigned(0, halfY * fy); + double oz = randomSigned(0, halfZ * fz); + + Vec3d center = new Vec3d(cx, cy, cz); + Vec3d jittered = center.add(ox, oy, oz); + return clampToBox(jittered, box, 1e-3); + } + + private static Vec3d clampToBox(Vec3d p, Box box) { + return clampToBox(p, box, 0); + } + + private static Vec3d clampToBox(Vec3d p, Box box, double inset) { + double minX = box.minX + inset, minY = box.minY + inset, minZ = box.minZ + inset; + double maxX = box.maxX - inset, maxY = box.maxY - inset, maxZ = box.maxZ - inset; + return new Vec3d( + MathHelper.clamp(p.x, minX, maxX), + MathHelper.clamp(p.y, minY, maxY), + MathHelper.clamp(p.z, minZ, maxZ) + ); + } + + private static double getMeleeReach(ClientPlayerEntity player) { + return player.getAbilities().creativeMode ? 5.0 : 3.0; + } + private static boolean isRangedStack(ItemStack stack) { - if (stack == null || stack.isEmpty()) return false; - Item item = stack.getItem(); - return (item instanceof BowItem) - || (item instanceof CrossbowItem) - || (item instanceof TridentItem) - || stack.isOf(Items.SNOWBALL) - || stack.isOf(Items.EGG) - || stack.isOf(Items.ENDER_PEARL); + return WeaponSelector.isRangedWeapon(stack); } public static AbstractClientPlayerEntity getBestTargetFor(ClientPlayerEntity player) { @@ -223,7 +268,7 @@ public static AbstractClientPlayerEntity getBestTargetFor(ClientPlayerEntity pla return Double.compare(p1Dist, p2Dist); }).filter(p -> { if (p == player) return false; - if (player.isTeammate(p) || ServerL.playerOnSameTeam(player, p)) return false; + if (!INSTANCE.testMode.getRawState() && (player.isTeammate(p) || ServerL.playerOnSameTeam(player, p))) return false; if (p.isDead() || p.getHealth() <= 0) return false; if (p.isInvulnerable()) return false; if (p.age < 20) return false; @@ -232,6 +277,46 @@ public static AbstractClientPlayerEntity getBestTargetFor(ClientPlayerEntity pla }).findFirst().orElse(null); } + public static LivingEntity getBestEntityTargetFor(ClientPlayerEntity player) { + ClientWorld world = (ClientWorld) player.getWorld(); + Vec3d eyePos = player.getEyePos(); + double maxRange = 6.0; + Box search = player.getBoundingBox().expand(maxRange + 1.0); + + List players = WorldL.getRealPlayers(); + List villagers = world.getEntitiesByClass(VillagerEntity.class, search, + v -> v.isAlive() && v.distanceTo(player) <= maxRange); + List traders = world.getEntitiesByClass(WanderingTraderEntity.class, search, + t -> t.isAlive() && t.distanceTo(player) <= maxRange); + List zombieVillagers = world.getEntitiesByClass(ZombieVillagerEntity.class, search, + z -> z.isAlive() && z.distanceTo(player) <= maxRange); + + List villagerLike = new ArrayList<>(); + villagerLike.addAll(villagers); + villagerLike.addAll(traders); + villagerLike.addAll(zombieVillagers); + + if (!villagerLike.isEmpty()) { + villagerLike.sort(Comparator.comparingDouble(e -> eyePos.squaredDistanceTo(e.getEyePos()))); + return villagerLike.get(0); + } + + List candidates = new ArrayList<>(); + for (AbstractClientPlayerEntity p : players) { + if (p == player) continue; + if (player.isTeammate(p) || ServerL.playerOnSameTeam(player, p)) continue; + if (p.isDead() || p.getHealth() <= 0) continue; + if (p.isInvulnerable()) continue; + if (p.age < 20) continue; + if (p.distanceTo(player) > maxRange) continue; + if (!player.canSee(p)) continue; + candidates.add(p); + } + if (candidates.isEmpty()) return null; + candidates.sort(Comparator.comparingDouble(e -> eyePos.squaredDistanceTo(e.getEyePos()))); + return candidates.get(0); + } + private static float wrapDegrees(float degrees) { return MathHelper.wrapDegrees(degrees); } @@ -261,6 +346,23 @@ private static void doSprint(boolean sprint) { } } + private void scheduleNextAttack(long nowMs) { + int cps = Math.max(1, attackCps.getRawState().intValue()); + double baseInterval = 1000.0 / cps; + double factor = 1.0 + ((Math.random() * 2 - 1) * ATTACK_VARIANCE); + long delay = (long) Math.max(35, baseInterval * factor); + nextAttackAtMs = nowMs + delay; + } + + private boolean isSword(ItemStack stack) { + return stack.isOf(Items.WOODEN_SWORD) || stack.isOf(Items.STONE_SWORD) || stack.isOf(Items.IRON_SWORD) || stack.isOf(Items.GOLDEN_SWORD) || stack.isOf(Items.DIAMOND_SWORD) || stack.isOf(Items.NETHERITE_SWORD); + } + + public static void playerBlockWithSword(ClientPlayerEntity player) { + player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.OFF_HAND, 1, + player.getYaw(), player.getPitch())); + } + @Override public boolean inValidGame() { return GameDetector.rootGame != GameDetector.ParentGame.ZOMBIES; diff --git a/src/main/resources/cigarette.accesswidener b/src/main/resources/cigarette.accesswidener new file mode 100644 index 00000000..b8bf59a8 --- /dev/null +++ b/src/main/resources/cigarette.accesswidener @@ -0,0 +1,19 @@ +accessWidener v1 named + +# USES https://raw.githubusercontent.com/CCBlueX/LiquidBounce/bee12584bfbc15c302803c7c40f9cdd797630244/src/main/resources/liquidbounce.accesswidener + +# This file is part of LiquidBounce (https://github.com/CCBlueX/LiquidBounce) +# +# Copyright (c) 2015 - 2025 CCBlueX +# +# LiquidBounce is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# LiquidBounce is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +accessible method net/minecraft/client/network/ClientPlayerInteractionManager sendSequencedPacket (Lnet/minecraft/client/world/ClientWorld;Lnet/minecraft/client/network/SequencedPacketCreator;)V \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 4e915f3b..2f31ffa3 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -30,5 +30,6 @@ "minecraft": "~1.21.5", "java": ">=21", "fabric-api": "*" - } + }, + "accessWidener": "cigarette.accesswidener" } \ No newline at end of file From 33ad0b30222be5983d6037b5141902456d5c9483 Mon Sep 17 00:00:00 2001 From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com> Date: Thu, 28 Aug 2025 09:08:50 -0400 Subject: [PATCH 06/23] cleanup redundant calls, methods, and comparisons --- .../dev/cigarette/lib/WeaponSelector.java | 75 ++----------------- .../cigarette/module/combat/PlayerAimbot.java | 40 +++------- 2 files changed, 15 insertions(+), 100 deletions(-) diff --git a/src/main/java/dev/cigarette/lib/WeaponSelector.java b/src/main/java/dev/cigarette/lib/WeaponSelector.java index d5b8bc45..c10a81e9 100644 --- a/src/main/java/dev/cigarette/lib/WeaponSelector.java +++ b/src/main/java/dev/cigarette/lib/WeaponSelector.java @@ -5,12 +5,13 @@ import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.item.*; import net.minecraft.item.tooltip.TooltipType; -import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; import org.jetbrains.annotations.Nullable; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; // Import ConcurrentHashMap +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -160,35 +161,6 @@ public static WeaponStats selectBestWeapon(ClientPlayerEntity player, @Nullable return bestWeapon; } - /** - * Selects the best weapon for auto-shooting based on situation - */ - @Nullable - public static WeaponStats selectBestWeapon(ClientPlayerEntity player) { - List weapons = analyzeWeapons(player); - if (weapons.isEmpty()) { - return null; - } - - // If no target, we can assume a default distance and no headshot possibility for scoring. - // double distance = (target != null) ? PlayerEntityL.getDistance(player, target) : 10.0; // Default distance - double distance = 10.0; // Default distance - - WeaponStats bestWeapon = null; - double bestScore = -1; - - for (WeaponStats weapon : weapons) { - double score = calculateWeaponScore(weapon, distance, true); - if (score > bestScore) { - bestScore = score; - bestWeapon = weapon; - } - } - return bestWeapon; - } - - - /** * Calculates weapon score based on situation */ @@ -225,18 +197,6 @@ private static double calculateWeaponScore(WeaponStats weapon, double distance, return score; } - private static double calculateWeaponScore(WeaponStats weapon, double distance, boolean canHeadshot) { - // Basic score is the weapon's DPS - double score = weapon.DPS; - - // Headshot bonus for high-damage weapons. - if (canHeadshot && weapon.damage > 15) { - score *= 1.2; - } - - return score; - } - /** * Switches to the best weapon if it's not already selected */ @@ -256,25 +216,6 @@ public static boolean switchToBestWeapon(ClientPlayerEntity player, @Nullable Zo return true; } - /** - * Switches to the best weapon if it's not already selected - */ - public static boolean switchToBestWeapon(ClientPlayerEntity player) { - WeaponStats bestWeapon = selectBestWeapon(player); - - if (bestWeapon == null) { - return false; - } - - int currentSlot = player.getInventory().getSelectedSlot(); - if (currentSlot == bestWeapon.slotIndex) { - return true; - } - - player.getInventory().setSelectedSlot(bestWeapon.slotIndex); - return true; - } - /** * Gets stats for currently held weapon */ @@ -313,12 +254,7 @@ public static void addCooldown(int slotIndex) { public static boolean isRangedWeapon(ItemStack stack) { if (stack == null || stack.isEmpty()) return false; Item item = stack.getItem(); - return (item instanceof BowItem) - || (item instanceof CrossbowItem) - || (item instanceof TridentItem) - || stack.isOf(Items.SNOWBALL) - || stack.isOf(Items.EGG) - || stack.isOf(Items.ENDER_PEARL); + return (item instanceof RangedWeaponItem) || (item instanceof ProjectileItem) || stack.isOf(Items.ENDER_PEARL); } /** @@ -371,7 +307,6 @@ private static int findBestMeleeSlot(ClientPlayerEntity player) { private static int meleeScore(ItemStack s) { if (s == null || s.isEmpty()) return Integer.MIN_VALUE; - Item item = s.getItem(); if (s.isOf(Items.NETHERITE_SWORD)) return 90; if (s.isOf(Items.DIAMOND_SWORD)) return 80; if (s.isOf(Items.IRON_SWORD)) return 70; diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index bde68ad6..3d19e88d 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -8,8 +8,6 @@ import dev.cigarette.lib.WeaponSelector; import dev.cigarette.lib.WorldL; import dev.cigarette.module.TickModule; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.AbstractClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity; @@ -20,17 +18,15 @@ import net.minecraft.entity.mob.ZombieVillagerEntity; import net.minecraft.entity.passive.VillagerEntity; import net.minecraft.entity.passive.WanderingTraderEntity; -import net.minecraft.item.*; -import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket; +import net.minecraft.item.ItemStack; import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket; +import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket; -import net.minecraft.registry.tag.BlockTags; +import net.minecraft.registry.tag.ItemTags; import net.minecraft.util.Hand; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.Box; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.Box; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -43,13 +39,13 @@ public class PlayerAimbot extends TickModule { // private final ToggleWidget silentAim = new ToggleWidget("Silent Aim", "Doesn't snap your camera client-side.").withDefaultState(true); private final ToggleWidget autoAttack = new ToggleWidget("Auto Attack", "Automatically hits players").withDefaultState(true); private final ToggleWidget autoWeaponSwitch = new ToggleWidget("Auto Weapon Switch", "Automatically switch weapons").withDefaultState(true); - public final SliderWidget smoothAim = new SliderWidget("Smooth Aim", "How quickly to snap to target in ticks").withBounds(1, 5, 20).withAccuracy(0); + public final SliderWidget smoothAim = new SliderWidget("Smooth Aim", "How quickly to snap to target in ticks").withBounds(1, 5, 20); public final ToggleWidget wTap = new ToggleWidget("W-Tap", "Automatically sprint before attacking").withDefaultState(false); public final SliderWidget.TwoHandedSlider jitterAmount = new SliderWidget.TwoHandedSlider("Jitter", "Random jitter range (min/max) degrees").withBounds(0, 0, 4).withAccuracy(2); - public final SliderWidget jitterSpeed = new SliderWidget("Jitter Speed", "How fast jitter target changes (ticks)").withBounds(5, 10, 40).withAccuracy(0); + public final SliderWidget jitterSpeed = new SliderWidget("Jitter Speed", "How fast jitter target changes (ticks)").withBounds(5, 10, 40); // private final ToggleWidget blockHit = new ToggleWidget("Block-Hit", "Briefly block just before attacking").withDefaultState(false); private final ToggleWidget testMode = new ToggleWidget("Test Mode", "Allows targeting villagers regardless of team").withDefaultState(false); - public final SliderWidget attackCps = new SliderWidget("Attack CPS", "Clicks per second for auto attack").withBounds(1, 8, 15).withAccuracy(0); + public final SliderWidget attackCps = new SliderWidget("Attack CPS", "Clicks per second for auto attack").withBounds(1, 8, 15); private KeyBinding rightClickKey = null; @@ -106,15 +102,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, boolean active = rightClickKey.isPressed() || autoAttack.getRawState(); if (active) { - HitResult hitResult = client.crosshairTarget; - if (hitResult != null && hitResult.getType() == HitResult.Type.BLOCK) { - BlockHitResult blockResult = (BlockHitResult) hitResult; - BlockState lookingAt = world.getBlockState(blockResult.getBlockPos()); - if (lookingAt.isIn(BlockTags.BUTTONS) || lookingAt.isOf(Blocks.CHEST)) return; - } - LivingEntity bestTarget = testMode.getRawState() ? getBestEntityTargetFor(player) : getBestTargetFor(player); - if (bestTarget == null) { hasLastAim = false; return; @@ -125,7 +113,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, WeaponSelector.switchToBestPvPWeapon(player, dist); } - boolean isRanged = isRangedStack(player.getMainHandStack()); + boolean isRanged = WeaponSelector.isRangedWeapon(player.getMainHandStack()); boolean shouldAttemptMelee = autoAttack.getRawState() && !isRanged; long now = System.currentTimeMillis(); boolean attackNow = shouldAttemptMelee && now >= nextAttackAtMs; @@ -156,7 +144,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, sendYaw = targetYaw; sendPitch = targetPitch; } else { - float yawDiff = wrapDegrees(targetYaw - lastAimYaw); + float yawDiff = MathHelper.wrapDegrees(targetYaw - lastAimYaw); float pitchDiff = targetPitch - lastAimPitch; float stepYaw = yawDiff / smoothTicks; float stepPitch = pitchDiff / smoothTicks; @@ -243,10 +231,6 @@ private static double getMeleeReach(ClientPlayerEntity player) { return player.getAbilities().creativeMode ? 5.0 : 3.0; } - private static boolean isRangedStack(ItemStack stack) { - return WeaponSelector.isRangedWeapon(stack); - } - public static AbstractClientPlayerEntity getBestTargetFor(ClientPlayerEntity player) { return WorldL.getRealPlayers().stream().sorted((p1, p2) -> { Vec3d eyePos = player.getEyePos(); @@ -317,10 +301,6 @@ public static LivingEntity getBestEntityTargetFor(ClientPlayerEntity player) { return candidates.get(0); } - private static float wrapDegrees(float degrees) { - return MathHelper.wrapDegrees(degrees); - } - private double randomSigned(double min, double max) { if (max <= 0) return 0; double magnitude = min + (Math.random() * (max - min)); @@ -355,7 +335,7 @@ private void scheduleNextAttack(long nowMs) { } private boolean isSword(ItemStack stack) { - return stack.isOf(Items.WOODEN_SWORD) || stack.isOf(Items.STONE_SWORD) || stack.isOf(Items.IRON_SWORD) || stack.isOf(Items.GOLDEN_SWORD) || stack.isOf(Items.DIAMOND_SWORD) || stack.isOf(Items.NETHERITE_SWORD); + return stack.isIn(ItemTags.SWORDS); } public static void playerBlockWithSword(ClientPlayerEntity player) { From 769d90cd9fa50d6b5a3f2d0559fde79f44a1630f Mon Sep 17 00:00:00 2001 From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com> Date: Thu, 28 Aug 2025 09:21:37 -0400 Subject: [PATCH 07/23] create weapon helper and refactor --- .../dev/cigarette/helper/WeaponHelper.java | 60 ++++++++++++++++ .../dev/cigarette/lib/WeaponSelector.java | 72 ------------------- .../cigarette/module/combat/PlayerAimbot.java | 38 ++++++---- 3 files changed, 86 insertions(+), 84 deletions(-) create mode 100644 src/main/java/dev/cigarette/helper/WeaponHelper.java diff --git a/src/main/java/dev/cigarette/helper/WeaponHelper.java b/src/main/java/dev/cigarette/helper/WeaponHelper.java new file mode 100644 index 00000000..4e8a59e2 --- /dev/null +++ b/src/main/java/dev/cigarette/helper/WeaponHelper.java @@ -0,0 +1,60 @@ +package dev.cigarette.helper; + +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.item.*; +import net.minecraft.registry.tag.ItemTags; + +public abstract class WeaponHelper { + public static boolean isRanged(ItemStack stack) { + if (stack == null || stack.isEmpty()) return false; + Item item = stack.getItem(); + return (item instanceof RangedWeaponItem) || (item instanceof ProjectileItem) || stack.isOf(Items.ENDER_PEARL); + } + + public static boolean isSword(ItemStack stack) { + return stack.isIn(ItemTags.SWORDS); + } + + public static int getBestRangedSlot(ClientPlayerEntity player) { + int best = -1; + for (int i = 0; i < 9; i++) { + ItemStack s = player.getInventory().getStack(i); + if (isRanged(s)) { + best = i; + // Prefer crossbow over bow if found later; for simplicity, last wins + } + } + return best; + } + + public static int getBestMeleeSlot(ClientPlayerEntity player) { + int best = -1; + int bestScore = Integer.MIN_VALUE; + for (int i = 0; i < 9; i++) { + ItemStack s = player.getInventory().getStack(i); + int score = getMeleeScore(s); + if (score > bestScore) { + bestScore = score; + best = i; + } + } + return (bestScore > Integer.MIN_VALUE) ? best : -1; + } + + public static int getMeleeScore(ItemStack s) { + if (s == null || s.isEmpty()) return Integer.MIN_VALUE; + if (s.isOf(Items.NETHERITE_SWORD)) return 90; + if (s.isOf(Items.DIAMOND_SWORD)) return 80; + if (s.isOf(Items.IRON_SWORD)) return 70; + if (s.isOf(Items.STONE_SWORD)) return 60; + if (s.isOf(Items.GOLDEN_SWORD)) return 55; + if (s.isOf(Items.WOODEN_SWORD)) return 50; + if (s.isOf(Items.NETHERITE_AXE)) return 75; + if (s.isOf(Items.DIAMOND_AXE)) return 68; + if (s.isOf(Items.IRON_AXE)) return 62; + if (s.isOf(Items.STONE_AXE)) return 56; + if (s.isOf(Items.GOLDEN_AXE)) return 50; + if (s.isOf(Items.WOODEN_AXE)) return 44; + return Integer.MIN_VALUE; + } +} diff --git a/src/main/java/dev/cigarette/lib/WeaponSelector.java b/src/main/java/dev/cigarette/lib/WeaponSelector.java index c10a81e9..9f1cf480 100644 --- a/src/main/java/dev/cigarette/lib/WeaponSelector.java +++ b/src/main/java/dev/cigarette/lib/WeaponSelector.java @@ -249,77 +249,5 @@ public static void addCooldown(int slotIndex) { } } } - - // Generic helper to detect ranged weapons in vanilla PvP contexts (non-Zombies) - public static boolean isRangedWeapon(ItemStack stack) { - if (stack == null || stack.isEmpty()) return false; - Item item = stack.getItem(); - return (item instanceof RangedWeaponItem) || (item instanceof ProjectileItem) || stack.isOf(Items.ENDER_PEARL); - } - - /** - * Switch to the best PvP weapon for players. For close range, prefer melee (best sword/axe in hotbar). - * For long range, prefer bow/crossbow if present. - * Returns true if a switch was made or the best weapon is already selected. - */ - public static boolean switchToBestPvPWeapon(ClientPlayerEntity player, double distanceToTarget) { - if (player == null) return false; - int current = player.getInventory().getSelectedSlot(); - int bestSlot; - if (distanceToTarget > 7.5) { - bestSlot = findBestRangedSlot(player); - if (bestSlot == -1) bestSlot = findBestMeleeSlot(player); - } else { - bestSlot = findBestMeleeSlot(player); - if (bestSlot == -1) bestSlot = findBestRangedSlot(player); - } - if (bestSlot == -1) return false; - if (bestSlot == current) return true; - player.getInventory().setSelectedSlot(bestSlot); - return true; - } - - private static int findBestRangedSlot(ClientPlayerEntity player) { - int best = -1; - for (int i = 0; i < 9; i++) { - ItemStack s = player.getInventory().getStack(i); - if (isRangedWeapon(s)) { - best = i; - // Prefer crossbow over bow if found later; for simplicity, last wins - } - } - return best; - } - - private static int findBestMeleeSlot(ClientPlayerEntity player) { - int best = -1; - int bestScore = Integer.MIN_VALUE; - for (int i = 0; i < 9; i++) { - ItemStack s = player.getInventory().getStack(i); - int score = meleeScore(s); - if (score > bestScore) { - bestScore = score; - best = i; - } - } - return (bestScore > Integer.MIN_VALUE) ? best : -1; - } - - private static int meleeScore(ItemStack s) { - if (s == null || s.isEmpty()) return Integer.MIN_VALUE; - if (s.isOf(Items.NETHERITE_SWORD)) return 90; - if (s.isOf(Items.DIAMOND_SWORD)) return 80; - if (s.isOf(Items.IRON_SWORD)) return 70; - if (s.isOf(Items.STONE_SWORD)) return 60; - if (s.isOf(Items.GOLDEN_SWORD)) return 55; - if (s.isOf(Items.WOODEN_SWORD)) return 50; - if (s.isOf(Items.NETHERITE_AXE)) return 75; - if (s.isOf(Items.DIAMOND_AXE)) return 68; - if (s.isOf(Items.IRON_AXE)) return 62; - if (s.isOf(Items.STONE_AXE)) return 56; - if (s.isOf(Items.GOLDEN_AXE)) return 50; - if (s.isOf(Items.WOODEN_AXE)) return 44; - return Integer.MIN_VALUE; - } } diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index 3d19e88d..4eb97c44 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -3,10 +3,8 @@ import dev.cigarette.GameDetector; import dev.cigarette.gui.widget.SliderWidget; import dev.cigarette.gui.widget.ToggleWidget; -import dev.cigarette.lib.InputOverride; -import dev.cigarette.lib.ServerL; -import dev.cigarette.lib.WeaponSelector; -import dev.cigarette.lib.WorldL; +import dev.cigarette.helper.WeaponHelper; +import dev.cigarette.lib.*; import dev.cigarette.module.TickModule; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.AbstractClientPlayerEntity; @@ -18,11 +16,9 @@ import net.minecraft.entity.mob.ZombieVillagerEntity; import net.minecraft.entity.passive.VillagerEntity; import net.minecraft.entity.passive.WanderingTraderEntity; -import net.minecraft.item.ItemStack; import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket; -import net.minecraft.registry.tag.ItemTags; import net.minecraft.util.Hand; import net.minecraft.util.math.Box; import net.minecraft.util.math.MathHelper; @@ -110,10 +106,10 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, if (autoWeaponSwitch.getRawState()) { double dist = player.distanceTo(bestTarget); - WeaponSelector.switchToBestPvPWeapon(player, dist); + switchToBestPvPWeapon(player, dist); } - boolean isRanged = WeaponSelector.isRangedWeapon(player.getMainHandStack()); + boolean isRanged = WeaponHelper.isRanged(player.getMainHandStack()); boolean shouldAttemptMelee = autoAttack.getRawState() && !isRanged; long now = System.currentTimeMillis(); boolean attackNow = shouldAttemptMelee && now >= nextAttackAtMs; @@ -334,15 +330,33 @@ private void scheduleNextAttack(long nowMs) { nextAttackAtMs = nowMs + delay; } - private boolean isSword(ItemStack stack) { - return stack.isIn(ItemTags.SWORDS); - } - public static void playerBlockWithSword(ClientPlayerEntity player) { player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.OFF_HAND, 1, player.getYaw(), player.getPitch())); } + /** + * Switch to the best PvP weapon for players. For close range, prefer melee (best sword/axe in hotbar). + * For long range, prefer bow/crossbow if present. + * Returns true if a switch was made or the best weapon is already selected. + */ + public static boolean switchToBestPvPWeapon(ClientPlayerEntity player, double distanceToTarget) { + if (player == null) return false; + int current = player.getInventory().getSelectedSlot(); + int bestSlot; + if (distanceToTarget > 7.5) { + bestSlot = WeaponHelper.getBestRangedSlot(player); + if (bestSlot == -1) bestSlot = WeaponHelper.getBestMeleeSlot(player); + } else { + bestSlot = WeaponHelper.getBestMeleeSlot(player); + if (bestSlot == -1) bestSlot = WeaponHelper.getBestRangedSlot(player); + } + if (bestSlot == -1) return false; + if (bestSlot == current) return true; + player.getInventory().setSelectedSlot(bestSlot); + return true; + } + @Override public boolean inValidGame() { return GameDetector.rootGame != GameDetector.ParentGame.ZOMBIES; From 536404abb57d5a8f6954541ebaa918f769be922b Mon Sep 17 00:00:00 2001 From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com> Date: Thu, 28 Aug 2025 09:29:43 -0400 Subject: [PATCH 08/23] simple refactor to save whitespace bytes --- .../cigarette/module/combat/PlayerAimbot.java | 130 +++++++++--------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index 4eb97c44..9e84e7df 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -96,83 +96,83 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, if (MinecraftClient.getInstance().currentScreen != null) return; - boolean active = rightClickKey.isPressed() || autoAttack.getRawState(); - if (active) { - LivingEntity bestTarget = testMode.getRawState() ? getBestEntityTargetFor(player) : getBestTargetFor(player); - if (bestTarget == null) { - hasLastAim = false; - return; - } - - if (autoWeaponSwitch.getRawState()) { - double dist = player.distanceTo(bestTarget); - switchToBestPvPWeapon(player, dist); - } - - boolean isRanged = WeaponHelper.isRanged(player.getMainHandStack()); - boolean shouldAttemptMelee = autoAttack.getRawState() && !isRanged; - long now = System.currentTimeMillis(); - boolean attackNow = shouldAttemptMelee && now >= nextAttackAtMs; - if (attackNow) scheduleNextAttack(now); + if(!rightClickKey.isPressed() && !autoAttack.getRawState()) { + hasLastAim = false; + return; + } - Vec3d aimPoint = getAimPointInsideHitbox(player, bestTarget, attackNow); - double reach = getMeleeReach(player); - boolean inReach = player.getEyePos().squaredDistanceTo(aimPoint) <= (reach * reach) + 1e-6; + LivingEntity bestTarget = testMode.getRawState() ? getBestEntityTargetFor(player) : getBestTargetFor(player); + if (bestTarget == null) { + hasLastAim = false; + return; + } - Vec3d vector = aimPoint.subtract(player.getEyePos()).normalize(); - float targetYaw = (float) Math.toDegrees(Math.atan2(-vector.x, vector.z)); - float targetPitch = (float) Math.toDegrees(Math.asin(-vector.y)); + if (autoWeaponSwitch.getRawState()) { + double dist = player.distanceTo(bestTarget); + switchToBestPvPWeapon(player, dist); + } - int smoothTicks = smoothAim.getRawState().intValue(); - float sendYaw; - float sendPitch; - if (attackNow) { + boolean isRanged = WeaponHelper.isRanged(player.getMainHandStack()); + boolean shouldAttemptMelee = autoAttack.getRawState() && !isRanged; + long now = System.currentTimeMillis(); + boolean attackNow = shouldAttemptMelee && now >= nextAttackAtMs; + if (attackNow) scheduleNextAttack(now); + + Vec3d aimPoint = getAimPointInsideHitbox(player, bestTarget, attackNow); + double reach = getMeleeReach(player); + boolean inReach = player.getEyePos().squaredDistanceTo(aimPoint) <= (reach * reach) + 1e-6; + + Vec3d vector = aimPoint.subtract(player.getEyePos()).normalize(); + float targetYaw = (float) Math.toDegrees(Math.atan2(-vector.x, vector.z)); + float targetPitch = (float) Math.toDegrees(Math.asin(-vector.y)); + + int smoothTicks = smoothAim.getRawState().intValue(); + float sendYaw; + float sendPitch; + if (attackNow) { + sendYaw = targetYaw; + sendPitch = targetPitch; + hasLastAim = true; + } else { + if (!hasLastAim) { + lastAimYaw = targetYaw; + lastAimPitch = targetPitch; + hasLastAim = true; + } + if (smoothTicks <= 1) { sendYaw = targetYaw; sendPitch = targetPitch; - hasLastAim = true; } else { - if (!hasLastAim) { - lastAimYaw = targetYaw; - lastAimPitch = targetPitch; - hasLastAim = true; - } - if (smoothTicks <= 1) { - sendYaw = targetYaw; - sendPitch = targetPitch; - } else { - float yawDiff = MathHelper.wrapDegrees(targetYaw - lastAimYaw); - float pitchDiff = targetPitch - lastAimPitch; - float stepYaw = yawDiff / smoothTicks; - float stepPitch = pitchDiff / smoothTicks; - sendYaw = lastAimYaw + stepYaw; - sendPitch = lastAimPitch + stepPitch; - } + float yawDiff = MathHelper.wrapDegrees(targetYaw - lastAimYaw); + float pitchDiff = targetPitch - lastAimPitch; + float stepYaw = yawDiff / smoothTicks; + float stepPitch = pitchDiff / smoothTicks; + sendYaw = lastAimYaw + stepYaw; + sendPitch = lastAimPitch + stepPitch; } - lastAimYaw = sendYaw; - lastAimPitch = sendPitch; + } + lastAimYaw = sendYaw; + lastAimPitch = sendPitch; - if (isRanged) { + if (isRanged) { + player.setYaw(sendYaw); + player.setPitch(sendPitch); + player.swingHand(Hand.MAIN_HAND); + } else { + if (attackNow && inReach) { + if (wTap.getRawState()) { + doSprint(false); + doSprint(true); + } player.setYaw(sendYaw); player.setPitch(sendPitch); + player.swingHand(Hand.MAIN_HAND); + player.networkHandler.sendPacket(PlayerInteractEntityC2SPacket.attack(bestTarget, player.isSneaking())); } else { - if (attackNow && inReach) { - if (wTap.getRawState()) { - doSprint(false); - doSprint(true); - } - player.setYaw(sendYaw); - player.setPitch(sendPitch); - - player.swingHand(Hand.MAIN_HAND); - player.networkHandler.sendPacket(PlayerInteractEntityC2SPacket.attack(bestTarget, player.isSneaking())); - } else { - player.setYaw(sendYaw); - player.setPitch(sendPitch); - } + player.setYaw(sendYaw); + player.setPitch(sendPitch); } - } else { - hasLastAim = false; } } @@ -302,7 +302,7 @@ private double randomSigned(double min, double max) { double magnitude = min + (Math.random() * (max - min)); return (Math.random() < 0.5 ? -1 : 1) * magnitude; } - + private static void doSprint(boolean sprint) { ClientPlayerEntity player = MinecraftClient.getInstance().player; if (player == null) return; From ba9be2fc5957a4bcdf344828c4d9898803215aa2 Mon Sep 17 00:00:00 2001 From: AH Date: Thu, 28 Aug 2025 22:50:38 -0400 Subject: [PATCH 09/23] fix(requires review): some flagging issues on matrix ac, refactor common aiming and randomization logic into AimingL for future aimbot implementations --- gradle.properties | 3 +- src/main/java/dev/cigarette/lib/AimingL.java | 281 ++++++++++++++++++ .../java/dev/cigarette/lib/PlayerEntityL.java | 13 +- src/main/java/dev/cigarette/lib/ServerL.java | 10 +- .../cigarette/module/combat/PlayerAimbot.java | 211 ++++--------- .../dev/cigarette/module/zombies/Aimbot.java | 17 +- .../cigarette/module/zombies/ReviveAura.java | 14 +- 7 files changed, 374 insertions(+), 175 deletions(-) create mode 100644 src/main/java/dev/cigarette/lib/AimingL.java diff --git a/gradle.properties b/gradle.properties index ede98d93..b7124134 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,6 @@ -org.gradle.jvmargs=-Xmx2G +org.gradle.jvmargs=-Xmx2G --enable-native-access=ALL-UNNAMED org.gradle.parallel=true +org.gradle.caching=true # Fabric Properties minecraft_version=1.21.5 diff --git a/src/main/java/dev/cigarette/lib/AimingL.java b/src/main/java/dev/cigarette/lib/AimingL.java new file mode 100644 index 00000000..44ca47ec --- /dev/null +++ b/src/main/java/dev/cigarette/lib/AimingL.java @@ -0,0 +1,281 @@ +package dev.cigarette.lib; + +import dev.cigarette.helper.WeaponHelper; +import dev.cigarette.mixin.ClientWorldAccessor; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.PendingUpdateManager; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.LivingEntity; +import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket; +import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket; +import net.minecraft.util.Hand; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + +public class AimingL { + /** + * Compute yaw/pitch angles (in degrees) to look from 'from' to 'to'. + * Yaw is in [-180..180], pitch is in [-90..90]. + */ + public static float[] anglesFromTo(Vec3d from, Vec3d to) { + Vec3d v = to.subtract(from).normalize(); + float yaw = (float) Math.toDegrees(Math.atan2(-v.x, v.z)); + float pitch = (float) Math.toDegrees(Math.asin(-v.y)); + return new float[]{yaw, pitch}; + } + + /** + * Smoothly step from lastYaw/lastPitch to targetYaw/targetPitch over smoothTicks steps, + * applying aimSmoothening in [0..0.9] as a final lerp factor to reduce abruptness. + * Returns the new (yaw, pitch) after one step. + */ + public static float[] smoothStep(float lastYaw, float lastPitch, float targetYaw, float targetPitch, int smoothTicks, double aimSmoothening) { + if (smoothTicks <= 1) return new float[]{targetYaw, targetPitch}; + float yawDiff = MathHelper.wrapDegrees(targetYaw - lastYaw); + float pitchDiff = targetPitch - lastPitch; + float stepYaw = yawDiff / smoothTicks; + float stepPitch = pitchDiff / smoothTicks; + float steppedYaw = lastYaw + stepYaw; + float steppedPitch = lastPitch + stepPitch; + double asm = Math.max(0.0, Math.min(0.9, aimSmoothening)); + double blend = 1.0 - asm; + float sendYaw = lastYaw + MathHelper.wrapDegrees(steppedYaw - lastYaw) * (float) blend; + float sendPitch = lastPitch + (steppedPitch - lastPitch) * (float) blend; + return new float[]{sendYaw, sendPitch}; + } + + /** + * Generate a random signed double in the range [-max, -min] U [min, max]. + * If max <= 0, returns 0.0. + */ + public static double randomSigned(double min, double max) { + if (max <= 0) return 0.0; + double magnitude = min + (Math.random() * (max - min)); + return (Math.random() < 0.5 ? -1 : 1) * magnitude; + } + + /** + * Clamp point p to be inside the given box, with the given inset from each face. + */ + public static Vec3d clampToBox(Vec3d p, Box box, double inset) { + double minX = box.minX + inset, minY = box.minY + inset, minZ = box.minZ + inset; + double maxX = box.maxX - inset, maxY = box.maxY - inset, maxZ = box.maxZ - inset; + return new Vec3d( + MathHelper.clamp(p.x, minX, maxX), + MathHelper.clamp(p.y, minY, maxY), + MathHelper.clamp(p.z, minZ, maxZ) + ); + } + + /** + * Clamp point p to be inside the given box, with no inset. + */ + public static Vec3d clampToBox(Vec3d p, Box box) { + return clampToBox(p, box, 0.0); + } + + /** + * Get an aim point inside the target's hitbox, with optional jitter when attacking. + * If attackNow is false, returns the closest point on the hitbox to the player's eye position. + * If attackNow is true, returns a jittered point towards the center of the hitbox. + * Jitter is controlled by jitterMinDeg, jitterMaxDeg and jitterCapDegrees parameters. + */ + public static Vec3d getAimPointInsideHitbox(ClientPlayerEntity player, LivingEntity target, boolean attackNow, + double jitterMinDeg, double jitterMaxDeg, double jitterCapDegrees) { + Box box = target.getBoundingBox(); + Vec3d eye = player.getEyePos(); + double cx = (box.minX + box.maxX) * 0.5; + double cy = (box.minY + box.maxY) * 0.5; + double cz = (box.minZ + box.maxZ) * 0.5; + double px = MathHelper.clamp(eye.x, box.minX, box.maxX); + double py = MathHelper.clamp(eye.y, box.minY, box.maxY); + double pz = MathHelper.clamp(eye.z, box.minZ, box.maxZ); + Vec3d closest = new Vec3d(px, py, pz); + Vec3d dirToCenter = new Vec3d(Math.copySign(1e-3, cx - px), Math.copySign(1e-3, cy - py), Math.copySign(1e-3, cz - pz)); + Vec3d basePoint = clampToBox(closest.add(dirToCenter), box); + + if (!attackNow) return basePoint; + + double halfX = (box.maxX - box.minX) * 0.5; + double halfY = (box.maxY - box.minY) * 0.5; + double halfZ = (box.maxZ - box.minZ) * 0.5; + double maxFrac = 0.3; + double jMin = Math.max(0.0, Math.min(jitterMinDeg, jitterCapDegrees)); + double jMax = Math.max(jMin, Math.min(jitterMaxDeg, jitterCapDegrees)); + double jitterScale = jMax <= 0 ? 0 : Math.min(1.0, jMax / jitterCapDegrees); + double fx = maxFrac * jitterScale; + double fy = (maxFrac * 0.6) * jitterScale; + double fz = maxFrac * jitterScale; + + double ox = randomSigned(0, halfX * fx); + double oy = randomSigned(0, halfY * fy); + double oz = randomSigned(0, halfZ * fz); + + Vec3d center = new Vec3d(cx, cy, cz); + Vec3d jittered = center.add(ox, oy, oz); + return clampToBox(jittered, box, 1e-3); + } + + /** + * Get the closest point on the target's body (feet, mid, head) to the attacker's eye position. + * This helps minimize the distance for aiming calculations. + */ + public static Vec3d getClosestBodyPos(LivingEntity from, LivingEntity to) { + Vec3d eyePos = from.getEyePos(); + Vec3d toPos = to.getPos(); + Vec3d toEyePos = to.getEyePos(); + Vec3d toFeetPos = new Vec3d(toPos.x, to.getY(), toPos.z); + Vec3d toMidPos = new Vec3d(toPos.x, to.getY() + to.getStandingEyeHeight() / 2, toPos.z); + Vec3d toHeadPos = new Vec3d(toPos.x, toEyePos.y, toPos.z); + Vec3d[] candidates = new Vec3d[]{toFeetPos, toMidPos, toHeadPos}; + Vec3d bestPos = toMidPos; + double bestDist = eyePos.distanceTo(bestPos); + for (Vec3d candidate : candidates) { + double dist = eyePos.distanceTo(candidate); + if (dist < bestDist) { + bestDist = dist; + bestPos = candidate; + } + } + return bestPos; + } + + /** + * Compute next attack delay in milliseconds based on CPS and variance. + * Ensures at least 35ms delay to avoid spamming. + * Variance is a fraction [0..1] representing max +/- variation around base interval. + */ + public static long computeNextAttackDelayMillis(int cps, double variance) { + int safeCps = Math.max(1, cps); + double baseInterval = 1000.0 / safeCps; + double factor = 1.0 + ((Math.random() * 2 - 1) * variance); + return (long) Math.max(35, baseInterval * factor); + } + + /** + * Compute next attack delay in milliseconds based on CPS, variance, and a minimum delay. + * Ensures at least 'min_delay'ms delay between attacks. + * Variance is a fraction [0..1] representing max +/- variation around base interval. + */ + public static long computeNextAttackDelayMillis(int cps, double variance, int min_delay) { + int safeCps = Math.max(1, cps); + double baseInterval = 1000.0 / safeCps; + double factor = 1.0 + ((Math.random() * 2 - 1) * variance); + return (long) Math.max(min_delay, baseInterval * factor); + } + + /** + * Start or stop sprinting based on the 'sprint' parameter. + * Only start sprinting if on ground and moving forward. + */ + public static void doSprint(boolean sprint) { + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (player == null) return; + KeyBinding forwardKey = KeyBinding.byId("key.forward"); + boolean forwardPressed = InputOverride.isActive ? InputOverride.forwardKey : (forwardKey != null && forwardKey.isPressed()); + if (!player.isOnGround() || !forwardPressed) return; + if (sprint) { + if (!player.isSprinting() && !player.isUsingItem() && !player.isSneaking()) { + player.setSprinting(true); + player.networkHandler.sendPacket(new ClientCommandC2SPacket(player, ClientCommandC2SPacket.Mode.START_SPRINTING)); + } + } else { + if (player.isSprinting()) { + player.setSprinting(false); + player.networkHandler.sendPacket(new ClientCommandC2SPacket(player, ClientCommandC2SPacket.Mode.STOP_SPRINTING)); + } + } + } + + /** + * Switch to the best PvP weapon based on distance to target. + * If distance > 7.5, prefer ranged weapon, else prefer melee weapon. + * If no suitable weapon found, do nothing. + * Returns true if a switch was made or already on best weapon, or false if no suitable weapon found. + */ + public static void switchToBestPvPWeapon(ClientPlayerEntity player, double distanceToTarget) { + if (player == null) return; + int current = player.getInventory().getSelectedSlot(); + int bestSlot; + if (distanceToTarget > 7.5) { + bestSlot = WeaponHelper.getBestRangedSlot(player); + if (bestSlot == -1) bestSlot = WeaponHelper.getBestMeleeSlot(player); + } else { + bestSlot = WeaponHelper.getBestMeleeSlot(player); + if (bestSlot == -1) bestSlot = WeaponHelper.getBestRangedSlot(player); + } + if (bestSlot == -1) return; + if (bestSlot == current) return; + player.getInventory().setSelectedSlot(bestSlot); + } + + /** + * Send a PlayerInteractItemC2SPacket with the given yaw/pitch and a new sequence number. + * Uses PendingUpdateManager to ensure proper sequencing. + */ + public static void sendAimPacket(ClientWorld world, ClientPlayerEntity player, float yaw, float pitch) { + if (world == null || player == null) return; + ClientWorldAccessor accessor = (ClientWorldAccessor) world; + try (PendingUpdateManager pum = accessor.getPendingUpdateManager().incrementSequence()) { + int seq = pum.getSequence(); + player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.MAIN_HAND, seq, yaw, pitch)); + } + } + + /** + * Compute low-frequency sway offsets (yaw, pitch) from a wave phase and amplitude. + */ + public static double[] computeSway(double wavePhase, double swayAmp) { + double swayYaw = Math.sin(wavePhase) * swayAmp; + double swayPitch = Math.cos(wavePhase * 0.9) * (swayAmp * 0.6); + return new double[]{swayYaw, swayPitch}; + } + + /** + * Interpolate 'current' towards 'target' using aimSmoothening in [0..0.9] as the lerp factor. + */ + public static double interpolateTowards(double current, double target, double aimSmoothening) { + double asm = Math.max(0.0, Math.min(0.9, aimSmoothening)); + return current + (target - current) * asm; + } + + /** + * Combine jitter, sway, micro-noise and aim-offset into final yaw/pitch offsets. Optionally cap when attacking. + */ + public static double[] combineOffsets(double jitterYaw, double jitterPitch, + double swayYaw, double swayPitch, + double microYaw, double microPitch, + double aimOffYaw, double aimOffPitch, + boolean attackNow, double attackCap) { + double combinedYaw = jitterYaw + swayYaw + microYaw + aimOffYaw; + double combinedPitch = jitterPitch + swayPitch + microPitch + aimOffPitch; + if (attackNow) { + double cap = Math.max(0.0, attackCap); + combinedYaw = Math.max(-cap, Math.min(cap, combinedYaw)); + combinedPitch = Math.max(-cap, Math.min(cap, combinedPitch)); + } + return new double[]{combinedYaw, combinedPitch}; + } + + /** + * Send a PlayerInteractEntityC2SPacket attack and swing the player's hand. + */ + public static void sendEntityAttack(ClientPlayerEntity player, LivingEntity target, boolean sneaking) { + if (player == null || target == null) return; + player.networkHandler.sendPacket(net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket.attack(target, sneaking)); + player.networkHandler.sendPacket(new net.minecraft.network.packet.c2s.play.HandSwingC2SPacket(net.minecraft.util.Hand.MAIN_HAND)); + } + + /** + * Send a look packet followed by an attack and a hand swing. Useful for revive/auras that need to set look client-side. + */ + public static void lookAndAttack(ClientWorld world, ClientPlayerEntity player, LivingEntity target, float yaw, float pitch) { + if (player == null || target == null || world == null) return; + player.networkHandler.sendPacket(new net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.LookAndOnGround(yaw, pitch, player.isOnGround(), player.horizontalCollision)); + player.networkHandler.sendPacket(net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket.attack(target, player.isSneaking())); + player.networkHandler.sendPacket(new net.minecraft.network.packet.c2s.play.HandSwingC2SPacket(net.minecraft.util.Hand.MAIN_HAND)); + } +} diff --git a/src/main/java/dev/cigarette/lib/PlayerEntityL.java b/src/main/java/dev/cigarette/lib/PlayerEntityL.java index a0ae42f5..24c219b2 100644 --- a/src/main/java/dev/cigarette/lib/PlayerEntityL.java +++ b/src/main/java/dev/cigarette/lib/PlayerEntityL.java @@ -5,9 +5,11 @@ import net.minecraft.item.ItemStack; import net.minecraft.registry.tag.ItemTags; import net.minecraft.util.math.Vec3d; -import org.jetbrains.annotations.Nullable; public class PlayerEntityL { + /* + * Returns yaw and pitch to look in the direction of the given vector. + */ public static float[] getRotationVectorInDirection(Vec3d vector) { Vec3d normalized = vector.normalize(); double pitchRadians = Math.asin(-normalized.y); @@ -19,13 +21,16 @@ public static float[] getRotationVectorInDirection(Vec3d vector) { return new float[]{yaw, pitch}; } + /* + * Sets the player's yaw and pitch to look in the direction of the given vector. + */ public static void setRotationVector(PlayerEntity player, Vec3d vector) { float[] yawPitch = getRotationVectorInDirection(vector); player.setYaw(yawPitch[0]); player.setPitch(yawPitch[1]); } - public static float getDistance(PlayerEntity player, PlayerEntity other) { - return (float) player.getPos().distanceTo(other.getPos()); - } +// public static float getDistance(PlayerEntity player, PlayerEntity other) { +// return (float) player.getPos().distanceTo(other.getPos()); +// } } diff --git a/src/main/java/dev/cigarette/lib/ServerL.java b/src/main/java/dev/cigarette/lib/ServerL.java index 366c4656..ff60e207 100644 --- a/src/main/java/dev/cigarette/lib/ServerL.java +++ b/src/main/java/dev/cigarette/lib/ServerL.java @@ -13,7 +13,11 @@ import java.util.Objects; public class ServerL { - // Generic variant usable on both client and server player entities. + /* + * Returns true if the two players are on the same team. + * Uses common Minecraft server logic. + * All individual checks are also exposed in ServerL. + */ public static boolean playerOnSameTeam(PlayerEntity player, PlayerEntity other) { if (player == null || other == null) return false; if (player.getScoreboardTeam() != null && other.getScoreboardTeam() != null && player.getScoreboardTeam().isEqual(other.getScoreboardTeam())) @@ -23,7 +27,6 @@ public static boolean playerOnSameTeam(PlayerEntity player, PlayerEntity other) getNameColor(player) != -1 && getNameColor(player) == getNameColor(other); } - // Backwards-compatible server-specific method (delegates to generic). public static boolean playerOnSameTeam(ServerPlayerEntity player, ServerPlayerEntity other) { return playerOnSameTeam((PlayerEntity) player, (PlayerEntity) other); } @@ -55,7 +58,7 @@ public static int getArmorColor(PlayerEntity player) { ItemStack boots = player.getInventory().getStack(36); ItemStack leggings = player.getInventory().getStack(37); - // Chestplate / helmet currently unused; keep commented for potential future logic. + // ItemStack chestplate = player.getInventory().getStack(38); // ItemStack helmet = player.getInventory().getStack(39); @@ -74,7 +77,6 @@ private static DyedColorComponent getDyedColorComponent(ItemStack stack) { return stack.getItem().getComponents().get(type); } - // Returns null (instead of crashing) when running on a remote/multiplayer server. public static ServerPlayerEntity getPlayer(ClientPlayerEntity player) { MinecraftClient mc = MinecraftClient.getInstance(); if (mc == null || mc.getServer() == null || player == null) return null; // mc.getServer() null on multiplayer clients diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index 9e84e7df..44a9ec99 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -16,12 +16,9 @@ import net.minecraft.entity.mob.ZombieVillagerEntity; import net.minecraft.entity.passive.VillagerEntity; import net.minecraft.entity.passive.WanderingTraderEntity; -import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket; -import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket; import net.minecraft.util.Hand; import net.minecraft.util.math.Box; -import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import org.jetbrains.annotations.NotNull; @@ -42,23 +39,35 @@ public class PlayerAimbot extends TickModule { // private final ToggleWidget blockHit = new ToggleWidget("Block-Hit", "Briefly block just before attacking").withDefaultState(false); private final ToggleWidget testMode = new ToggleWidget("Test Mode", "Allows targeting villagers regardless of team").withDefaultState(false); public final SliderWidget attackCps = new SliderWidget("Attack CPS", "Clicks per second for auto attack").withBounds(1, 8, 15); - + private final SliderWidget aimSmoothening = new SliderWidget("Aim Smoothing", "How much to smooth/interpolate between steps of the smooth aim").withBounds(0, 0.05, 0.9).withAccuracy(3); + public final SliderWidget.TwoHandedSlider aimOffset = new SliderWidget.TwoHandedSlider("Aim Offset", "Aim target offset (min/max) degrees").withBounds(0, 0, 3).withAccuracy(2); + public final SliderWidget swayAmount = new SliderWidget("Sway Amount", "Continuous sway amplitude (degrees)").withBounds(0, 0.0, 1.5).withAccuracy(2); + public final SliderWidget swaySpeed = new SliderWidget("Sway Speed", "How fast the sway oscillates (ticks)").withBounds(20, 40, 200); private KeyBinding rightClickKey = null; private boolean hasLastAim = false; private float lastAimYaw = 0f; private float lastAimPitch = 0f; + private int tickCount = 0; + private double currentJitterYaw = 0.0, currentJitterPitch = 0.0; + private double nextJitterYaw = 0.0, nextJitterPitch = 0.0; + private double wavePhase = 0.0; + private long nextAttackAtMs = 0L; private static final double ATTACK_VARIANCE = 0.15; private static final double JITTER_CAP_DEGREES = 2.0; private PlayerAimbot(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(autoAttack, autoWeaponSwitch, smoothAim, jitterAmount, jitterSpeed, wTap, testMode, attackCps); + this.setChildren(autoAttack, autoWeaponSwitch, smoothAim, aimSmoothening, jitterAmount, jitterSpeed, aimOffset, swayAmount, swaySpeed, wTap, testMode, attackCps); autoAttack.registerConfigKey(id + ".autoAttack"); autoWeaponSwitch.registerConfigKey(id + ".autoWeaponSwitch"); smoothAim.registerConfigKey(id + ".smoothAim"); + aimSmoothening.registerConfigKey(id + ".aimSmoothening"); + aimOffset.registerConfigKey(id + ".aimOffset"); + swayAmount.registerConfigKey(id + ".swayAmount"); + swaySpeed.registerConfigKey(id + ".swaySpeed"); wTap.registerConfigKey(id + ".wTap"); jitterAmount.registerConfigKey(id + ".jitter"); jitterSpeed.registerConfigKey(id + ".jitterSpeed"); @@ -67,28 +76,24 @@ private PlayerAimbot(String id, String name, String tooltip) { attackCps.registerConfigKey(id + ".attackCps"); } - private static Vec3d getClosestBodyPos(LivingEntity from, LivingEntity to) { - Vec3d eyePos = from.getEyePos(); - Vec3d toPos = to.getPos(); - Vec3d toEyePos = to.getEyePos(); - Vec3d toFeetPos = new Vec3d(toPos.x, to.getY(), toPos.z); - Vec3d toMidPos = new Vec3d(toPos.x, to.getY() + to.getStandingEyeHeight() / 2, toPos.z); - Vec3d toHeadPos = new Vec3d(toPos.x, toEyePos.y, toPos.z); - Vec3d[] candidates = new Vec3d[] {toFeetPos, toMidPos, toHeadPos}; - Vec3d bestPos = toMidPos; - double bestDist = eyePos.distanceTo(bestPos); - for (Vec3d candidate : candidates) { - double dist = eyePos.distanceTo(candidate); - if (dist < bestDist) { - bestDist = dist; - bestPos = candidate; - } - } - return bestPos; - } - @Override protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { + tickCount++; + double swaySpd = Math.max(1.0, swaySpeed.getRawState().doubleValue()); + wavePhase += (2.0 * Math.PI) / swaySpd; + + int jitterTicks = Math.max(1, jitterSpeed.getRawState().intValue()); + if (tickCount % jitterTicks == 0) { + double jMin = Math.max(0.0, Math.min(jitterAmount.getMinValue(), JITTER_CAP_DEGREES)); + double jMax = Math.max(jMin, Math.min(jitterAmount.getMaxValue(), JITTER_CAP_DEGREES)); + nextJitterYaw = AimingL.randomSigned(jMin, jMax); + nextJitterPitch = AimingL.randomSigned(jMin * 0.6, jMax * 0.6); // pitch jitter typically smaller + } + // interpolate jitter targets towards the next jitter using AimingL + double asmVal = aimSmoothening.getRawState().doubleValue(); + currentJitterYaw = AimingL.interpolateTowards(currentJitterYaw, nextJitterYaw, asmVal); + currentJitterPitch = AimingL.interpolateTowards(currentJitterPitch, nextJitterPitch, asmVal); + if (rightClickKey == null) { rightClickKey = KeyBinding.byId("key.use"); return; @@ -109,22 +114,23 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, if (autoWeaponSwitch.getRawState()) { double dist = player.distanceTo(bestTarget); - switchToBestPvPWeapon(player, dist); + AimingL.switchToBestPvPWeapon(player, dist); } boolean isRanged = WeaponHelper.isRanged(player.getMainHandStack()); boolean shouldAttemptMelee = autoAttack.getRawState() && !isRanged; long now = System.currentTimeMillis(); boolean attackNow = shouldAttemptMelee && now >= nextAttackAtMs; - if (attackNow) scheduleNextAttack(now); + if (attackNow) nextAttackAtMs = now + AimingL.computeNextAttackDelayMillis(attackCps.getRawState().intValue(), ATTACK_VARIANCE); - Vec3d aimPoint = getAimPointInsideHitbox(player, bestTarget, attackNow); + Vec3d aimPoint = AimingL.getAimPointInsideHitbox(player, bestTarget, attackNow, + jitterAmount.getMinValue(), jitterAmount.getMaxValue(), JITTER_CAP_DEGREES); double reach = getMeleeReach(player); boolean inReach = player.getEyePos().squaredDistanceTo(aimPoint) <= (reach * reach) + 1e-6; - Vec3d vector = aimPoint.subtract(player.getEyePos()).normalize(); - float targetYaw = (float) Math.toDegrees(Math.atan2(-vector.x, vector.z)); - float targetPitch = (float) Math.toDegrees(Math.asin(-vector.y)); + float[] angles = AimingL.anglesFromTo(player.getEyePos(), aimPoint); + float targetYaw = angles[0]; + float targetPitch = angles[1]; int smoothTicks = smoothAim.getRawState().intValue(); float sendYaw; @@ -143,17 +149,35 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, sendYaw = targetYaw; sendPitch = targetPitch; } else { - float yawDiff = MathHelper.wrapDegrees(targetYaw - lastAimYaw); - float pitchDiff = targetPitch - lastAimPitch; - float stepYaw = yawDiff / smoothTicks; - float stepPitch = pitchDiff / smoothTicks; - sendYaw = lastAimYaw + stepYaw; - sendPitch = lastAimPitch + stepPitch; + float[] sm = AimingL.smoothStep(lastAimYaw, lastAimPitch, targetYaw, targetPitch, smoothTicks, aimSmoothening.getRawState().doubleValue()); + sendYaw = sm[0]; + sendPitch = sm[1]; } } lastAimYaw = sendYaw; lastAimPitch = sendPitch; + double swayAmp = swayAmount.getRawState().doubleValue(); + double[] sway = AimingL.computeSway(wavePhase, swayAmp); + double swayYaw = sway[0]; + double swayPitch = sway[1]; + double microNoiseYaw = AimingL.randomSigned(0.0, 0.02); // tiny micro movements + double microNoisePitch = AimingL.randomSigned(0.0, 0.02); + + double aoMin = Math.max(0.0, Math.min(aimOffset.getMinValue(), 5.0)); + double aoMax = Math.max(aoMin, Math.min(aimOffset.getMaxValue(), 5.0)); + double aimOffYaw = AimingL.randomSigned(aoMin, aoMax); + double aimOffPitch = AimingL.randomSigned(aoMin * 0.6, aoMax * 0.6); + + double attackCap = Math.max(0.5, Math.min(2.0, aimOffset.getMaxValue())); + double[] combined = AimingL.combineOffsets(currentJitterYaw, currentJitterPitch, + swayYaw, swayPitch, + microNoiseYaw, microNoisePitch, + aimOffYaw, aimOffPitch, + attackNow, attackCap); + sendYaw = (float) (sendYaw + combined[0]); + sendPitch = (float) (sendPitch + combined[1]); + if (isRanged) { player.setYaw(sendYaw); player.setPitch(sendPitch); @@ -161,14 +185,14 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, } else { if (attackNow && inReach) { if (wTap.getRawState()) { - doSprint(false); - doSprint(true); + AimingL.doSprint(false); + AimingL.doSprint(true); } player.setYaw(sendYaw); player.setPitch(sendPitch); - player.swingHand(Hand.MAIN_HAND); - player.networkHandler.sendPacket(PlayerInteractEntityC2SPacket.attack(bestTarget, player.isSneaking())); + // centralized attack + swing + AimingL.sendEntityAttack(player, bestTarget, player.isSneaking()); } else { player.setYaw(sendYaw); player.setPitch(sendPitch); @@ -176,53 +200,6 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, } } - private Vec3d getAimPointInsideHitbox(ClientPlayerEntity player, LivingEntity target, boolean attackNow) { - Box box = target.getBoundingBox(); - Vec3d eye = player.getEyePos(); - double cx = (box.minX + box.maxX) * 0.5; double cy = (box.minY + box.maxY) * 0.5; double cz = (box.minZ + box.maxZ) * 0.5; - double px = MathHelper.clamp(eye.x, box.minX, box.maxX); - double py = MathHelper.clamp(eye.y, box.minY, box.maxY); - double pz = MathHelper.clamp(eye.z, box.minZ, box.maxZ); - Vec3d closest = new Vec3d(px, py, pz); - Vec3d dirToCenter = new Vec3d(Math.copySign(1e-3, cx - px), Math.copySign(1e-3, cy - py), Math.copySign(1e-3, cz - pz)); - Vec3d basePoint = clampToBox(closest.add(dirToCenter), box); - - if (!attackNow) return basePoint; - - double halfX = (box.maxX - box.minX) * 0.5; - double halfY = (box.maxY - box.minY) * 0.5; - double halfZ = (box.maxZ - box.minZ) * 0.5; - double maxFrac = 0.3; - double jMin = Math.max(0.0, Math.min(jitterAmount.getMinValue(), JITTER_CAP_DEGREES)); - double jMax = Math.max(jMin, Math.min(jitterAmount.getMaxValue(), JITTER_CAP_DEGREES)); - double jitterScale = jMax <= 0 ? 0 : Math.min(1.0, jMax / JITTER_CAP_DEGREES); - double fx = maxFrac * jitterScale; - double fy = (maxFrac * 0.6) * jitterScale; - double fz = maxFrac * jitterScale; - - double ox = randomSigned(0, halfX * fx); - double oy = randomSigned(0, halfY * fy); - double oz = randomSigned(0, halfZ * fz); - - Vec3d center = new Vec3d(cx, cy, cz); - Vec3d jittered = center.add(ox, oy, oz); - return clampToBox(jittered, box, 1e-3); - } - - private static Vec3d clampToBox(Vec3d p, Box box) { - return clampToBox(p, box, 0); - } - - private static Vec3d clampToBox(Vec3d p, Box box, double inset) { - double minX = box.minX + inset, minY = box.minY + inset, minZ = box.minZ + inset; - double maxX = box.maxX - inset, maxY = box.maxY - inset, maxZ = box.maxZ - inset; - return new Vec3d( - MathHelper.clamp(p.x, minX, maxX), - MathHelper.clamp(p.y, minY, maxY), - MathHelper.clamp(p.z, minZ, maxZ) - ); - } - private static double getMeleeReach(ClientPlayerEntity player) { return player.getAbilities().creativeMode ? 5.0 : 3.0; } @@ -297,65 +274,7 @@ public static LivingEntity getBestEntityTargetFor(ClientPlayerEntity player) { return candidates.get(0); } - private double randomSigned(double min, double max) { - if (max <= 0) return 0; - double magnitude = min + (Math.random() * (max - min)); - return (Math.random() < 0.5 ? -1 : 1) * magnitude; - } - - private static void doSprint(boolean sprint) { - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (player == null) return; - KeyBinding forwardKey = KeyBinding.byId("key.forward"); - boolean forwardPressed = InputOverride.isActive ? InputOverride.forwardKey : (forwardKey != null && forwardKey.isPressed()); - if (!player.isOnGround() || !forwardPressed) return; - if (sprint) { - if (!player.isSprinting() && !player.isUsingItem() && !player.isSneaking()) { - player.setSprinting(true); - player.networkHandler.sendPacket(new ClientCommandC2SPacket(player, ClientCommandC2SPacket.Mode.START_SPRINTING)); - } - } else { - if (player.isSprinting()) { - player.setSprinting(false); - player.networkHandler.sendPacket(new ClientCommandC2SPacket(player, ClientCommandC2SPacket.Mode.STOP_SPRINTING)); - } - } - } - private void scheduleNextAttack(long nowMs) { - int cps = Math.max(1, attackCps.getRawState().intValue()); - double baseInterval = 1000.0 / cps; - double factor = 1.0 + ((Math.random() * 2 - 1) * ATTACK_VARIANCE); - long delay = (long) Math.max(35, baseInterval * factor); - nextAttackAtMs = nowMs + delay; - } - - public static void playerBlockWithSword(ClientPlayerEntity player) { - player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.OFF_HAND, 1, - player.getYaw(), player.getPitch())); - } - - /** - * Switch to the best PvP weapon for players. For close range, prefer melee (best sword/axe in hotbar). - * For long range, prefer bow/crossbow if present. - * Returns true if a switch was made or the best weapon is already selected. - */ - public static boolean switchToBestPvPWeapon(ClientPlayerEntity player, double distanceToTarget) { - if (player == null) return false; - int current = player.getInventory().getSelectedSlot(); - int bestSlot; - if (distanceToTarget > 7.5) { - bestSlot = WeaponHelper.getBestRangedSlot(player); - if (bestSlot == -1) bestSlot = WeaponHelper.getBestMeleeSlot(player); - } else { - bestSlot = WeaponHelper.getBestMeleeSlot(player); - if (bestSlot == -1) bestSlot = WeaponHelper.getBestRangedSlot(player); - } - if (bestSlot == -1) return false; - if (bestSlot == current) return true; - player.getInventory().setSelectedSlot(bestSlot); - return true; - } @Override public boolean inValidGame() { diff --git a/src/main/java/dev/cigarette/module/zombies/Aimbot.java b/src/main/java/dev/cigarette/module/zombies/Aimbot.java index e5d2bded..e050ae67 100644 --- a/src/main/java/dev/cigarette/module/zombies/Aimbot.java +++ b/src/main/java/dev/cigarette/module/zombies/Aimbot.java @@ -5,20 +5,16 @@ import dev.cigarette.gui.widget.SliderWidget; import dev.cigarette.gui.widget.ToggleWidget; import dev.cigarette.lib.PlayerEntityL; +import dev.cigarette.lib.AimingL; import dev.cigarette.lib.WeaponSelector; -import dev.cigarette.mixin.ClientWorldAccessor; import dev.cigarette.module.TickModule; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.network.PendingUpdateManager; import net.minecraft.client.option.KeyBinding; import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.EntityPose; -import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket; import net.minecraft.registry.tag.BlockTags; -import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.Vec3d; @@ -76,18 +72,15 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, WeaponSelector.addCooldown(player.getInventory().getSelectedSlot()); - float aimYaw = (float) Math.toDegrees(Math.atan2(-vector.x, vector.z)); - float aimPitch = (float) Math.toDegrees(Math.asin(-vector.y)); + float[] angles = AimingL.anglesFromTo(player.getEyePos(), predictedPos); + float aimYaw = angles[0]; + float aimPitch = angles[1]; if (!silentAim.getRawState()) { PlayerEntityL.setRotationVector(player, vector); } - ClientWorldAccessor clientWorldAccessor = (ClientWorldAccessor) world; - try (PendingUpdateManager pendingUpdateManager = clientWorldAccessor.getPendingUpdateManager().incrementSequence()) { - int seq = pendingUpdateManager.getSequence(); - player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.MAIN_HAND, seq, aimYaw, aimPitch)); - } + AimingL.sendAimPacket(world, player, aimYaw, aimPitch); } } diff --git a/src/main/java/dev/cigarette/module/zombies/ReviveAura.java b/src/main/java/dev/cigarette/module/zombies/ReviveAura.java index 7ef23bba..893df53e 100644 --- a/src/main/java/dev/cigarette/module/zombies/ReviveAura.java +++ b/src/main/java/dev/cigarette/module/zombies/ReviveAura.java @@ -15,8 +15,8 @@ import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket; -import net.minecraft.util.Hand; import net.minecraft.util.math.Vec3d; +import dev.cigarette.lib.AimingL; import org.jetbrains.annotations.NotNull; public class ReviveAura extends RenderModule { @@ -111,14 +111,12 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, } Vec3d targetCenterPos = target.getBoundingBox().getCenter(); - Vec3d direction = targetCenterPos.subtract(player.getEyePos()).normalize(); + float[] angles = AimingL.anglesFromTo(player.getEyePos(), targetCenterPos); + float aimYaw = angles[0]; + float aimPitch = angles[1]; - float aimYaw = (float) Math.toDegrees(Math.atan2(-direction.x, direction.z)); - float aimPitch = (float) Math.toDegrees(Math.asin(-direction.y)); - - player.networkHandler.sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(aimYaw, aimPitch, player.isOnGround(), player.horizontalCollision)); - player.networkHandler.sendPacket(PlayerInteractEntityC2SPacket.attack(target, player.isSneaking())); - player.networkHandler.sendPacket(new HandSwingC2SPacket(Hand.MAIN_HAND)); + // Use centralized helper for look+attack+hand swing + AimingL.lookAndAttack(world, player, target, aimYaw, aimPitch); cooldownTicks += 25; } From 5af592009c3e3891ed63130a68e2303be549befa Mon Sep 17 00:00:00 2001 From: AH Date: Sun, 31 Aug 2025 12:12:18 -0400 Subject: [PATCH 10/23] feat: flags a lot. requires further testing. bezier control thing okr suggested --- src/main/java/dev/cigarette/lib/AimingL.java | 221 +++----- .../java/dev/cigarette/mixin/EntityMixin.java | 13 + .../cigarette/module/combat/PlayerAimbot.java | 488 ++++++++++-------- 3 files changed, 370 insertions(+), 352 deletions(-) diff --git a/src/main/java/dev/cigarette/lib/AimingL.java b/src/main/java/dev/cigarette/lib/AimingL.java index 44ca47ec..956251be 100644 --- a/src/main/java/dev/cigarette/lib/AimingL.java +++ b/src/main/java/dev/cigarette/lib/AimingL.java @@ -7,6 +7,9 @@ import net.minecraft.client.network.PendingUpdateManager; import net.minecraft.client.option.KeyBinding; import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.util.hit.HitResult; +import net.minecraft.world.RaycastContext; import net.minecraft.entity.LivingEntity; import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket; import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket; @@ -14,6 +17,7 @@ import net.minecraft.util.math.Box; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; +import org.jetbrains.annotations.NotNull; public class AimingL { /** @@ -21,32 +25,30 @@ public class AimingL { * Yaw is in [-180..180], pitch is in [-90..90]. */ public static float[] anglesFromTo(Vec3d from, Vec3d to) { - Vec3d v = to.subtract(from).normalize(); + Vec3d d = to.subtract(from); + double lenSq = d.lengthSquared(); + if (!isFinite(d.x) || !isFinite(d.y) || !isFinite(d.z) || lenSq < 1.0e-12) { + ClientPlayerEntity p = MinecraftClient.getInstance().player; + float fallbackYaw = p != null ? MathHelper.wrapDegrees(p.getYaw()) : 0f; + float fallbackPitch = p != null ? MathHelper.clamp(p.getPitch(), -90f, 90f) : 0f; + return new float[]{fallbackYaw, fallbackPitch}; + } + Vec3d v = d.normalize(); float yaw = (float) Math.toDegrees(Math.atan2(-v.x, v.z)); float pitch = (float) Math.toDegrees(Math.asin(-v.y)); + yaw = sanitizeYaw(yaw); + pitch = sanitizePitch(pitch); + yaw = limitAccuracy(yaw, 100); + pitch = limitAccuracy(pitch, 100); + if (!Float.isFinite(yaw) || !Float.isFinite(pitch)) { + ClientPlayerEntity p = MinecraftClient.getInstance().player; + float fallbackYaw = p != null ? MathHelper.wrapDegrees(p.getYaw()) : 0f; + float fallbackPitch = p != null ? MathHelper.clamp(p.getPitch(), -90f, 90f) : 0f; + return new float[]{fallbackYaw, fallbackPitch}; + } return new float[]{yaw, pitch}; } - /** - * Smoothly step from lastYaw/lastPitch to targetYaw/targetPitch over smoothTicks steps, - * applying aimSmoothening in [0..0.9] as a final lerp factor to reduce abruptness. - * Returns the new (yaw, pitch) after one step. - */ - public static float[] smoothStep(float lastYaw, float lastPitch, float targetYaw, float targetPitch, int smoothTicks, double aimSmoothening) { - if (smoothTicks <= 1) return new float[]{targetYaw, targetPitch}; - float yawDiff = MathHelper.wrapDegrees(targetYaw - lastYaw); - float pitchDiff = targetPitch - lastPitch; - float stepYaw = yawDiff / smoothTicks; - float stepPitch = pitchDiff / smoothTicks; - float steppedYaw = lastYaw + stepYaw; - float steppedPitch = lastPitch + stepPitch; - double asm = Math.max(0.0, Math.min(0.9, aimSmoothening)); - double blend = 1.0 - asm; - float sendYaw = lastYaw + MathHelper.wrapDegrees(steppedYaw - lastYaw) * (float) blend; - float sendPitch = lastPitch + (steppedPitch - lastPitch) * (float) blend; - return new float[]{sendYaw, sendPitch}; - } - /** * Generate a random signed double in the range [-max, -min] U [min, max]. * If max <= 0, returns 0.0. @@ -87,84 +89,43 @@ public static Vec3d getAimPointInsideHitbox(ClientPlayerEntity player, LivingEnt double jitterMinDeg, double jitterMaxDeg, double jitterCapDegrees) { Box box = target.getBoundingBox(); Vec3d eye = player.getEyePos(); - double cx = (box.minX + box.maxX) * 0.5; - double cy = (box.minY + box.maxY) * 0.5; - double cz = (box.minZ + box.maxZ) * 0.5; - double px = MathHelper.clamp(eye.x, box.minX, box.maxX); - double py = MathHelper.clamp(eye.y, box.minY, box.maxY); - double pz = MathHelper.clamp(eye.z, box.minZ, box.maxZ); - Vec3d closest = new Vec3d(px, py, pz); - Vec3d dirToCenter = new Vec3d(Math.copySign(1e-3, cx - px), Math.copySign(1e-3, cy - py), Math.copySign(1e-3, cz - pz)); - Vec3d basePoint = clampToBox(closest.add(dirToCenter), box); - - if (!attackNow) return basePoint; - - double halfX = (box.maxX - box.minX) * 0.5; - double halfY = (box.maxY - box.minY) * 0.5; - double halfZ = (box.maxZ - box.minZ) * 0.5; - double maxFrac = 0.3; - double jMin = Math.max(0.0, Math.min(jitterMinDeg, jitterCapDegrees)); - double jMax = Math.max(jMin, Math.min(jitterMaxDeg, jitterCapDegrees)); - double jitterScale = jMax <= 0 ? 0 : Math.min(1.0, jMax / jitterCapDegrees); - double fx = maxFrac * jitterScale; - double fy = (maxFrac * 0.6) * jitterScale; - double fz = maxFrac * jitterScale; - - double ox = randomSigned(0, halfX * fx); - double oy = randomSigned(0, halfY * fy); - double oz = randomSigned(0, halfZ * fz); - - Vec3d center = new Vec3d(cx, cy, cz); - Vec3d jittered = center.add(ox, oy, oz); - return clampToBox(jittered, box, 1e-3); - } - - /** - * Get the closest point on the target's body (feet, mid, head) to the attacker's eye position. - * This helps minimize the distance for aiming calculations. - */ - public static Vec3d getClosestBodyPos(LivingEntity from, LivingEntity to) { - Vec3d eyePos = from.getEyePos(); - Vec3d toPos = to.getPos(); - Vec3d toEyePos = to.getEyePos(); - Vec3d toFeetPos = new Vec3d(toPos.x, to.getY(), toPos.z); - Vec3d toMidPos = new Vec3d(toPos.x, to.getY() + to.getStandingEyeHeight() / 2, toPos.z); - Vec3d toHeadPos = new Vec3d(toPos.x, toEyePos.y, toPos.z); - Vec3d[] candidates = new Vec3d[]{toFeetPos, toMidPos, toHeadPos}; - Vec3d bestPos = toMidPos; - double bestDist = eyePos.distanceTo(bestPos); - for (Vec3d candidate : candidates) { - double dist = eyePos.distanceTo(candidate); - if (dist < bestDist) { - bestDist = dist; - bestPos = candidate; + double minX = box.minX, minY = box.minY, minZ = box.minZ; + double maxX = box.maxX, maxY = box.maxY, maxZ = box.maxZ; + + if (!attackNow) { + double vy = minY + (maxY - minY) * 0.5; + double vx = (minX + maxX) * 0.5; + double vz = (minZ + maxZ) * 0.5; + Vec3d center = new Vec3d(vx, vy, vz); + if (center.squaredDistanceTo(eye) < 1.0e-12) { + center = center.add(0.0, 1.0e-4, 0.0); } + return center; } - return bestPos; - } - /** - * Compute next attack delay in milliseconds based on CPS and variance. - * Ensures at least 35ms delay to avoid spamming. - * Variance is a fraction [0..1] representing max +/- variation around base interval. - */ - public static long computeNextAttackDelayMillis(int cps, double variance) { - int safeCps = Math.max(1, cps); - double baseInterval = 1000.0 / safeCps; - double factor = 1.0 + ((Math.random() * 2 - 1) * variance); - return (long) Math.max(35, baseInterval * factor); - } + double height = maxY - minY; + double region = Math.random(); + double pickY; + if (region < 0.6) { + pickY = minY + height * (0.4 + Math.random() * 0.2); + } else if (region < 0.85) { + pickY = minY + height * (0.78 + Math.random() * 0.18); + } else { + pickY = minY + height * (0.12 + Math.random() * 0.18); + } - /** - * Compute next attack delay in milliseconds based on CPS, variance, and a minimum delay. - * Ensures at least 'min_delay'ms delay between attacks. - * Variance is a fraction [0..1] representing max +/- variation around base interval. - */ - public static long computeNextAttackDelayMillis(int cps, double variance, int min_delay) { - int safeCps = Math.max(1, cps); - double baseInterval = 1000.0 / safeCps; - double factor = 1.0 + ((Math.random() * 2 - 1) * variance); - return (long) Math.max(min_delay, baseInterval * factor); + double cx = (minX + maxX) * 0.5; + double cz = (minZ + maxZ) * 0.5; + double halfX = (maxX - minX) * 0.5; + double halfZ = (maxZ - minZ) * 0.5; + double ox = (Math.random() - 0.5) * halfX * 0.6; + double oz = (Math.random() - 0.5) * halfZ * 0.6; + + Vec3d pick = new Vec3d(cx + ox, pickY, cz + oz); + if (pick.squaredDistanceTo(eye) < 1.0e-12) { + pick = pick.add(0.0, 1.0e-4, 0.0); + } + return clampToBox(pick, box, 1e-3); } /** @@ -218,64 +179,40 @@ public static void switchToBestPvPWeapon(ClientPlayerEntity player, double dista */ public static void sendAimPacket(ClientWorld world, ClientPlayerEntity player, float yaw, float pitch) { if (world == null || player == null) return; + float syaw = sanitizeYaw(yaw); + float spitch = sanitizePitch(pitch); ClientWorldAccessor accessor = (ClientWorldAccessor) world; try (PendingUpdateManager pum = accessor.getPendingUpdateManager().incrementSequence()) { int seq = pum.getSequence(); - player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.MAIN_HAND, seq, yaw, pitch)); + player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.MAIN_HAND, seq, syaw, spitch)); } } /** - * Compute low-frequency sway offsets (yaw, pitch) from a wave phase and amplitude. + * Send a look packet followed by an attack and a hand swing. Useful for revive/auras that need to set look client-side. */ - public static double[] computeSway(double wavePhase, double swayAmp) { - double swayYaw = Math.sin(wavePhase) * swayAmp; - double swayPitch = Math.cos(wavePhase * 0.9) * (swayAmp * 0.6); - return new double[]{swayYaw, swayPitch}; + public static void lookAndAttack(ClientWorld world, ClientPlayerEntity player, LivingEntity target, float yaw, float pitch) { + if (player == null || target == null || world == null) return; + player.attack(target); + player.swingHand(Hand.MAIN_HAND); } - /** - * Interpolate 'current' towards 'target' using aimSmoothening in [0..0.9] as the lerp factor. - */ - public static double interpolateTowards(double current, double target, double aimSmoothening) { - double asm = Math.max(0.0, Math.min(0.9, aimSmoothening)); - return current + (target - current) * asm; + // Helpers + private static boolean isFinite(double d) { + return !Double.isNaN(d) && !Double.isInfinite(d); } - - /** - * Combine jitter, sway, micro-noise and aim-offset into final yaw/pitch offsets. Optionally cap when attacking. - */ - public static double[] combineOffsets(double jitterYaw, double jitterPitch, - double swayYaw, double swayPitch, - double microYaw, double microPitch, - double aimOffYaw, double aimOffPitch, - boolean attackNow, double attackCap) { - double combinedYaw = jitterYaw + swayYaw + microYaw + aimOffYaw; - double combinedPitch = jitterPitch + swayPitch + microPitch + aimOffPitch; - if (attackNow) { - double cap = Math.max(0.0, attackCap); - combinedYaw = Math.max(-cap, Math.min(cap, combinedYaw)); - combinedPitch = Math.max(-cap, Math.min(cap, combinedPitch)); - } - return new double[]{combinedYaw, combinedPitch}; + private static float sanitizeYaw(float yaw) { + if (!Float.isFinite(yaw)) yaw = 0f; + return MathHelper.wrapDegrees(yaw); } - - /** - * Send a PlayerInteractEntityC2SPacket attack and swing the player's hand. - */ - public static void sendEntityAttack(ClientPlayerEntity player, LivingEntity target, boolean sneaking) { - if (player == null || target == null) return; - player.networkHandler.sendPacket(net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket.attack(target, sneaking)); - player.networkHandler.sendPacket(new net.minecraft.network.packet.c2s.play.HandSwingC2SPacket(net.minecraft.util.Hand.MAIN_HAND)); + // Keep for API parity even if unused internally + private static float limitAccuracy(float yaw, int precision) { + if (precision <= 0) return yaw; + float factor = 1f / precision; + return Math.round(yaw * precision) * factor; } - - /** - * Send a look packet followed by an attack and a hand swing. Useful for revive/auras that need to set look client-side. - */ - public static void lookAndAttack(ClientWorld world, ClientPlayerEntity player, LivingEntity target, float yaw, float pitch) { - if (player == null || target == null || world == null) return; - player.networkHandler.sendPacket(new net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.LookAndOnGround(yaw, pitch, player.isOnGround(), player.horizontalCollision)); - player.networkHandler.sendPacket(net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket.attack(target, player.isSneaking())); - player.networkHandler.sendPacket(new net.minecraft.network.packet.c2s.play.HandSwingC2SPacket(net.minecraft.util.Hand.MAIN_HAND)); + private static float sanitizePitch(float pitch) { + if (!Float.isFinite(pitch)) pitch = 0f; + return MathHelper.clamp(pitch, -90f, 90f); } -} +} \ No newline at end of file diff --git a/src/main/java/dev/cigarette/mixin/EntityMixin.java b/src/main/java/dev/cigarette/mixin/EntityMixin.java index e3d4b26e..21716af1 100644 --- a/src/main/java/dev/cigarette/mixin/EntityMixin.java +++ b/src/main/java/dev/cigarette/mixin/EntityMixin.java @@ -5,6 +5,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.UUID; @@ -32,4 +33,16 @@ private void getTeamColorValue(CallbackInfoReturnable cir) { cir.setReturnValue(Glow.getGlowColor(uuid)); } } + + @Inject(method = "setYaw", at = @At("HEAD")) + public void setYaw(float yaw, CallbackInfo ci) { + if (yaw > 360) yaw = yaw % 360; + if (yaw < 0) yaw = 360 + (yaw % 360); + } + + @Inject(method = "setPitch", at = @At("HEAD")) + public void setPitch(float pitch, CallbackInfo ci) { + if (pitch > 90) pitch = 90; + if (pitch < -90) pitch = -90; + } } diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index 44a9ec99..9c594ec6 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -3,278 +3,346 @@ import dev.cigarette.GameDetector; import dev.cigarette.gui.widget.SliderWidget; import dev.cigarette.gui.widget.ToggleWidget; -import dev.cigarette.helper.WeaponHelper; -import dev.cigarette.lib.*; +import dev.cigarette.lib.AimingL; +import dev.cigarette.lib.ServerL; import dev.cigarette.module.TickModule; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.AbstractClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.option.KeyBinding; import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.EntityPose; import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.mob.ZombieVillagerEntity; import net.minecraft.entity.passive.VillagerEntity; -import net.minecraft.entity.passive.WanderingTraderEntity; -import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket; -import net.minecraft.util.Hand; -import net.minecraft.util.math.Box; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Objects; +import java.util.Random; +import java.util.UUID; +import java.util.stream.Stream; public class PlayerAimbot extends TickModule { public static final PlayerAimbot INSTANCE = new PlayerAimbot("combat.playerAimbot", "PlayerAimbot", "Automatically aims at players."); -// private final ToggleWidget silentAim = new ToggleWidget("Silent Aim", "Doesn't snap your camera client-side.").withDefaultState(true); private final ToggleWidget autoAttack = new ToggleWidget("Auto Attack", "Automatically hits players").withDefaultState(true); - private final ToggleWidget autoWeaponSwitch = new ToggleWidget("Auto Weapon Switch", "Automatically switch weapons").withDefaultState(true); public final SliderWidget smoothAim = new SliderWidget("Smooth Aim", "How quickly to snap to target in ticks").withBounds(1, 5, 20); public final ToggleWidget wTap = new ToggleWidget("W-Tap", "Automatically sprint before attacking").withDefaultState(false); - public final SliderWidget.TwoHandedSlider jitterAmount = new SliderWidget.TwoHandedSlider("Jitter", "Random jitter range (min/max) degrees").withBounds(0, 0, 4).withAccuracy(2); - public final SliderWidget jitterSpeed = new SliderWidget("Jitter Speed", "How fast jitter target changes (ticks)").withBounds(5, 10, 40); -// private final ToggleWidget blockHit = new ToggleWidget("Block-Hit", "Briefly block just before attacking").withDefaultState(false); private final ToggleWidget testMode = new ToggleWidget("Test Mode", "Allows targeting villagers regardless of team").withDefaultState(false); + public final SliderWidget attackCps = new SliderWidget("Attack CPS", "Clicks per second for auto attack").withBounds(1, 8, 15); - private final SliderWidget aimSmoothening = new SliderWidget("Aim Smoothing", "How much to smooth/interpolate between steps of the smooth aim").withBounds(0, 0.05, 0.9).withAccuracy(3); - public final SliderWidget.TwoHandedSlider aimOffset = new SliderWidget.TwoHandedSlider("Aim Offset", "Aim target offset (min/max) degrees").withBounds(0, 0, 3).withAccuracy(2); - public final SliderWidget swayAmount = new SliderWidget("Sway Amount", "Continuous sway amplitude (degrees)").withBounds(0, 0.0, 1.5).withAccuracy(2); - public final SliderWidget swaySpeed = new SliderWidget("Sway Speed", "How fast the sway oscillates (ticks)").withBounds(20, 40, 200); - private KeyBinding rightClickKey = null; - private boolean hasLastAim = false; - private float lastAimYaw = 0f; - private float lastAimPitch = 0f; + public final ToggleWidget ignoreTeammates = new ToggleWidget("Ignore Teammates", "Don't target players on your team").withDefaultState(true); + + public final SliderWidget jitterViolence = new SliderWidget("Jitter Aggression", "Scale of aim jitter (0 disables)").withBounds(0.0, 1.0, 3.0).withAccuracy(2); + public final SliderWidget driftViolence = new SliderWidget("Drift Aggression", "Scale of slow drift vs jitter").withBounds(0.0, 0.6, 2.0).withAccuracy(2); + public final SliderWidget aimToleranceDeg = new SliderWidget("Aim Tolerance (deg)", "Max degrees off for attack").withBounds(0.5, 2.0, 6.0).withAccuracy(1); - private int tickCount = 0; - private double currentJitterYaw = 0.0, currentJitterPitch = 0.0; - private double nextJitterYaw = 0.0, nextJitterPitch = 0.0; - private double wavePhase = 0.0; + public final SliderWidget smoothingMultiplier = new SliderWidget("Smoothing Multiplier", "Scales duration based on angle").withBounds(0.5, 1.0, 3.0).withAccuracy(2); + public final SliderWidget bezierInfluence = new SliderWidget("Bezier Influence", "0 = linear, 1 = full bezier curve").withBounds(0.0, 1.0, 1.0).withAccuracy(2); + public final SliderWidget controlJitterScale = new SliderWidget("Control Jitter", "Randomness of control points").withBounds(0.0, 1.0, 3.0).withAccuracy(2); + public final SliderWidget interpolationMode = new SliderWidget("Interpolation Mode", "0=Linear 1=Cubic 2=Cos 3=Smoothstep").withBounds(0, 1, 3).withAccuracy(0); - private long nextAttackAtMs = 0L; - private static final double ATTACK_VARIANCE = 0.15; - private static final double JITTER_CAP_DEGREES = 2.0; + public final SliderWidget interferenceAngleDeg = new SliderWidget("Interference Angle (deg)", "Delta that triggers pause").withBounds(2.0, 6.0, 20.0).withAccuracy(1); + public final SliderWidget interferenceGraceTicks = new SliderWidget("Interference Grace (ticks)", "Pause length while moving").withBounds(2, 8, 30).withAccuracy(0); + + public final SliderWidget aimRange = new SliderWidget("Aim Range", "Maximum range to target players").withBounds(3, 6, 20).withAccuracy(1); private PlayerAimbot(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(autoAttack, autoWeaponSwitch, smoothAim, aimSmoothening, jitterAmount, jitterSpeed, aimOffset, swayAmount, swaySpeed, wTap, testMode, attackCps); + this.setChildren(autoAttack, smoothAim, aimRange, wTap, attackCps, ignoreTeammates, testMode, + jitterViolence, driftViolence, aimToleranceDeg, smoothingMultiplier, bezierInfluence, controlJitterScale, interpolationMode, + interferenceAngleDeg, interferenceGraceTicks); autoAttack.registerConfigKey(id + ".autoAttack"); - autoWeaponSwitch.registerConfigKey(id + ".autoWeaponSwitch"); smoothAim.registerConfigKey(id + ".smoothAim"); - aimSmoothening.registerConfigKey(id + ".aimSmoothening"); - aimOffset.registerConfigKey(id + ".aimOffset"); - swayAmount.registerConfigKey(id + ".swayAmount"); - swaySpeed.registerConfigKey(id + ".swaySpeed"); + aimRange.registerConfigKey(id + ".aimRange"); wTap.registerConfigKey(id + ".wTap"); - jitterAmount.registerConfigKey(id + ".jitter"); - jitterSpeed.registerConfigKey(id + ".jitterSpeed"); -// blockHit.registerConfigKey(id + ".blockHit"); - testMode.registerConfigKey(id + ".testMode"); attackCps.registerConfigKey(id + ".attackCps"); + ignoreTeammates.registerConfigKey(id + ".ignoreTeammates"); + testMode.registerConfigKey(id + ".testMode"); + jitterViolence.registerConfigKey(id + ".jitterViolence"); + driftViolence.registerConfigKey(id + ".driftViolence"); + aimToleranceDeg.registerConfigKey(id + ".aimToleranceDeg"); + smoothingMultiplier.registerConfigKey(id + ".smoothingMultiplier"); + bezierInfluence.registerConfigKey(id + ".bezierInfluence"); + controlJitterScale.registerConfigKey(id + ".controlJitterScale"); + interpolationMode.registerConfigKey(id + ".interpolationMode"); + interferenceAngleDeg.registerConfigKey(id + ".interferenceAngleDeg"); + interferenceGraceTicks.registerConfigKey(id + ".interferenceGraceTicks"); } + // Bezier aim plan state + private @Nullable LivingEntity activeTarget = null; + private @Nullable UUID activeTargetId = null; + private int planTicksTotal = 0; + private int planTicksElapsed = 0; + private float startYaw = 0f, startPitch = 0f; + private float c1Yaw = 0f, c1Pitch = 0f; + private float c2Yaw = 0f, c2Pitch = 0f; + private float endYaw = 0f, endPitch = 0f; + private final Random rng = new Random(); + + // CPS scheduling + private int ticksUntilNextAttack = 0; + + // Reach assumption (entityAttackRange not exposed here) + private static final double MELEE_REACH = 3.2; + + private int suppressTicks = 0; + private boolean lastAppliedValid = false; + private float lastAppliedYaw = 0f, lastAppliedPitch = 0f; + @Override protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { - tickCount++; - double swaySpd = Math.max(1.0, swaySpeed.getRawState().doubleValue()); - wavePhase += (2.0 * Math.PI) / swaySpd; - - int jitterTicks = Math.max(1, jitterSpeed.getRawState().intValue()); - if (tickCount % jitterTicks == 0) { - double jMin = Math.max(0.0, Math.min(jitterAmount.getMinValue(), JITTER_CAP_DEGREES)); - double jMax = Math.max(jMin, Math.min(jitterAmount.getMaxValue(), JITTER_CAP_DEGREES)); - nextJitterYaw = AimingL.randomSigned(jMin, jMax); - nextJitterPitch = AimingL.randomSigned(jMin * 0.6, jMax * 0.6); // pitch jitter typically smaller - } - // interpolate jitter targets towards the next jitter using AimingL - double asmVal = aimSmoothening.getRawState().doubleValue(); - currentJitterYaw = AimingL.interpolateTowards(currentJitterYaw, nextJitterYaw, asmVal); - currentJitterPitch = AimingL.interpolateTowards(currentJitterPitch, nextJitterPitch, asmVal); + if (MinecraftClient.getInstance().currentScreen != null) return; + + if (handleInterferenceSuppression(player)) return; - if (rightClickKey == null) { - rightClickKey = KeyBinding.byId("key.use"); + LivingEntity target = pickOrValidateTarget(world, player); + if (target == null) { + clearPlan(); return; } - if (MinecraftClient.getInstance().currentScreen != null) return; + Vec3d aimPoint = computeAimPoint(player, target, false); + float[] targetAngles = AimingL.anglesFromTo(player.getEyePos(), aimPoint); + float curYaw = MathHelper.wrapDegrees(player.getYaw()); + float curPitch = MathHelper.clamp(player.getPitch(), -90f, 90f); - if(!rightClickKey.isPressed() && !autoAttack.getRawState()) { - hasLastAim = false; - return; + if (!isPlanActive() || !Objects.equals(target.getUuid(), activeTargetId) || planTargetChangedSignificantly(targetAngles)) { + buildBezierPlan(curYaw, curPitch, targetAngles[0], targetAngles[1]); + activeTarget = target; + activeTargetId = target.getUuid(); } - LivingEntity bestTarget = testMode.getRawState() ? getBestEntityTargetFor(player) : getBestTargetFor(player); - if (bestTarget == null) { - hasLastAim = false; - return; + float[] stepAngles = evalPlanStep(); + player.setYaw(stepAngles[0]); + player.setPitch(stepAngles[1]); + lastAppliedYaw = player.getYaw(); + lastAppliedPitch = player.getPitch(); + lastAppliedValid = true; + + if (autoAttack.getRawState()) { + if (ticksUntilNextAttack > 0) ticksUntilNextAttack--; + boolean withinRange = player.distanceTo(target) <= MELEE_REACH; + boolean aimAligned = aimAlignmentOk(player, target); + if (withinRange && aimAligned && ticksUntilNextAttack <= 0) { + if (wTap.getRawState()) AimingL.doSprint(true); + Vec3d atkAim = computeAimPoint(player, target, true); + float[] atkAngles = AimingL.anglesFromTo(player.getEyePos(), atkAim); + AimingL.lookAndAttack(world, player, target, atkAngles[0], atkAngles[1]); + scheduleNextAttack(); + } } + } - if (autoWeaponSwitch.getRawState()) { - double dist = player.distanceTo(bestTarget); - AimingL.switchToBestPvPWeapon(player, dist); - } + private boolean handleInterferenceSuppression(ClientPlayerEntity player) { + float yaw = player.getYaw(); + float pitch = player.getPitch(); + float angleDelta = Math.max(Math.abs(MathHelper.wrapDegrees(yaw - lastAppliedYaw)), Math.abs(pitch - lastAppliedPitch)); + float trigger = (float) Math.max(0.5, interferenceAngleDeg.getRawState()); - boolean isRanged = WeaponHelper.isRanged(player.getMainHandStack()); - boolean shouldAttemptMelee = autoAttack.getRawState() && !isRanged; - long now = System.currentTimeMillis(); - boolean attackNow = shouldAttemptMelee && now >= nextAttackAtMs; - if (attackNow) nextAttackAtMs = now + AimingL.computeNextAttackDelayMillis(attackCps.getRawState().intValue(), ATTACK_VARIANCE); - - Vec3d aimPoint = AimingL.getAimPointInsideHitbox(player, bestTarget, attackNow, - jitterAmount.getMinValue(), jitterAmount.getMaxValue(), JITTER_CAP_DEGREES); - double reach = getMeleeReach(player); - boolean inReach = player.getEyePos().squaredDistanceTo(aimPoint) <= (reach * reach) + 1e-6; - - float[] angles = AimingL.anglesFromTo(player.getEyePos(), aimPoint); - float targetYaw = angles[0]; - float targetPitch = angles[1]; - - int smoothTicks = smoothAim.getRawState().intValue(); - float sendYaw; - float sendPitch; - if (attackNow) { - sendYaw = targetYaw; - sendPitch = targetPitch; - hasLastAim = true; - } else { - if (!hasLastAim) { - lastAimYaw = targetYaw; - lastAimPitch = targetPitch; - hasLastAim = true; - } - if (smoothTicks <= 1) { - sendYaw = targetYaw; - sendPitch = targetPitch; + if (suppressTicks > 0) { + if (angleDelta > trigger) { + suppressTicks = (int) Math.max(1, Math.round(interferenceGraceTicks.getRawState())); } else { - float[] sm = AimingL.smoothStep(lastAimYaw, lastAimPitch, targetYaw, targetPitch, smoothTicks, aimSmoothening.getRawState().doubleValue()); - sendYaw = sm[0]; - sendPitch = sm[1]; + suppressTicks--; } + lastAppliedYaw = yaw; + lastAppliedPitch = pitch; + lastAppliedValid = true; + return true; } - lastAimYaw = sendYaw; - lastAimPitch = sendPitch; - - double swayAmp = swayAmount.getRawState().doubleValue(); - double[] sway = AimingL.computeSway(wavePhase, swayAmp); - double swayYaw = sway[0]; - double swayPitch = sway[1]; - double microNoiseYaw = AimingL.randomSigned(0.0, 0.02); // tiny micro movements - double microNoisePitch = AimingL.randomSigned(0.0, 0.02); - - double aoMin = Math.max(0.0, Math.min(aimOffset.getMinValue(), 5.0)); - double aoMax = Math.max(aoMin, Math.min(aimOffset.getMaxValue(), 5.0)); - double aimOffYaw = AimingL.randomSigned(aoMin, aoMax); - double aimOffPitch = AimingL.randomSigned(aoMin * 0.6, aoMax * 0.6); - - double attackCap = Math.max(0.5, Math.min(2.0, aimOffset.getMaxValue())); - double[] combined = AimingL.combineOffsets(currentJitterYaw, currentJitterPitch, - swayYaw, swayPitch, - microNoiseYaw, microNoisePitch, - aimOffYaw, aimOffPitch, - attackNow, attackCap); - sendYaw = (float) (sendYaw + combined[0]); - sendPitch = (float) (sendPitch + combined[1]); - - if (isRanged) { - player.setYaw(sendYaw); - player.setPitch(sendPitch); - player.swingHand(Hand.MAIN_HAND); - } else { - if (attackNow && inReach) { - if (wTap.getRawState()) { - AimingL.doSprint(false); - AimingL.doSprint(true); - } - player.setYaw(sendYaw); - player.setPitch(sendPitch); - // centralized attack + swing - AimingL.sendEntityAttack(player, bestTarget, player.isSneaking()); - } else { - player.setYaw(sendYaw); - player.setPitch(sendPitch); - } + if (lastAppliedValid && angleDelta > trigger) { + suppressTicks = (int) Math.max(1, Math.round(interferenceGraceTicks.getRawState())); + lastAppliedYaw = yaw; + lastAppliedPitch = pitch; + return true; } + return false; + } + + private void scheduleNextAttack() { + double cps = Math.max(1.0, attackCps.getRawState()); + double idealTicks = 20.0 / cps; + double jitter = idealTicks * (0.15 + rng.nextDouble() * 0.20); + ticksUntilNextAttack = (int) Math.max(1, Math.round(idealTicks + (rng.nextBoolean() ? jitter : -jitter * 0.5))); + } + + private boolean aimAlignmentOk(ClientPlayerEntity player, LivingEntity target) { + Vec3d aimPoint = computeAimPoint(player, target, false); + float[] want = AimingL.anglesFromTo(player.getEyePos(), aimPoint); + float dyaw = MathHelper.wrapDegrees(player.getYaw() - want[0]); + float dpitch = want[1] - player.getPitch(); + float tol = (float) Math.max(0.1, aimToleranceDeg.getRawState()); + return Math.abs(dyaw) < tol && Math.abs(dpitch) < tol; } - private static double getMeleeReach(ClientPlayerEntity player) { - return player.getAbilities().creativeMode ? 5.0 : 3.0; + private float[] evalPlanStep() { + if (!isPlanActive()) return new float[]{startYaw, startPitch}; + planTicksElapsed = Math.min(planTicksElapsed + 1, planTicksTotal); + float t = planTicksTotal == 0 ? 1f : (float) planTicksElapsed / (float) planTicksTotal; + float et = applyInterpolation(t); + float linYaw = startYaw + (endYaw - startYaw) * et; + float linPitch = startPitch + (endPitch - startPitch) * et; + float bezYaw = cubicBezier(startYaw, c1Yaw, c2Yaw, endYaw, et); + float bezPitch = cubicBezier(startPitch, c1Pitch, c2Pitch, endPitch, et); + float inf = (float) Math.max(0.0, Math.min(1.0, bezierInfluence.getRawState())); + float yaw = linYaw + (bezYaw - linYaw) * inf; + float pitch = linPitch + (bezPitch - linPitch) * inf; + + float angleSpan = Math.abs(MathHelper.wrapDegrees(startYaw - endYaw)) + Math.abs(endPitch - startPitch); + float baseJitter = Math.max(0.0f, Math.min(0.5f, angleSpan * 0.003f)); + float jitterMag = (float) (baseJitter * Math.max(0.0, jitterViolence.getRawState())); + float driftMag = (float) (jitterMag * Math.max(0.0, driftViolence.getRawState())); + float fineJitterYaw = (float) (rng.nextGaussian() * jitterMag * (1.0 - et)); + float fineJitterPitch = (float) (rng.nextGaussian() * jitterMag * (1.0 - et)); + float driftYaw = (float) (rng.nextGaussian() * driftMag * 0.2); + float driftPitch = (float) (rng.nextGaussian() * driftMag * 0.2); + + float outYaw = MathHelper.wrapDegrees(yaw + fineJitterYaw + driftYaw); + float outPitch = MathHelper.clamp(pitch + fineJitterPitch + driftPitch, -90f, 90f); + return new float[]{outYaw, outPitch}; } - public static AbstractClientPlayerEntity getBestTargetFor(ClientPlayerEntity player) { - return WorldL.getRealPlayers().stream().sorted((p1, p2) -> { - Vec3d eyePos = player.getEyePos(); - Vec3d p1Pos = p1.getEyePos().subtract(eyePos); - Vec3d p2Pos = p2.getEyePos().subtract(eyePos); - double p1Dist = p1Pos.lengthSquared(); - double p2Dist = p2Pos.lengthSquared(); - - if (p1.isSneaking() && !p2.isSneaking()) p1Dist *= 0.8; - else if (!p1.isSneaking() && p2.isSneaking()) p2Dist *= 0.8; - - if (p1.getPose() == EntityPose.CROUCHING && p2.getPose() != EntityPose.CROUCHING) p1Dist *= 0.9; - else if (p1.getPose() != EntityPose.CROUCHING && p2.getPose() == EntityPose.CROUCHING) p2Dist *= 0.9; - - if (p1.isSprinting() && !p2.isSprinting()) p1Dist *= 1.1; - else if (!p1.isSprinting() && p2.isSprinting()) p2Dist *= 1.1; - if (p1.getVelocity().lengthSquared() > 0.01 && p2.getVelocity().lengthSquared() < 0.01) p1Dist *= 1.1; - else if (p1.getVelocity().lengthSquared() < 0.01 && p2.getVelocity().lengthSquared() > 0.01) p2Dist *= 1.1; - return Double.compare(p1Dist, p2Dist); - }).filter(p -> { - if (p == player) return false; - if (!INSTANCE.testMode.getRawState() && (player.isTeammate(p) || ServerL.playerOnSameTeam(player, p))) return false; - if (p.isDead() || p.getHealth() <= 0) return false; - if (p.isInvulnerable()) return false; - if (p.age < 20) return false; - if (p.distanceTo(player) > 6) return false; - return player.canSee(p); - }).findFirst().orElse(null); + private void buildBezierPlan(float curYaw, float curPitch, float targetYaw, float targetPitch) { + float tYaw = unwrapTowards(targetYaw, curYaw); + startYaw = curYaw; + startPitch = curPitch; + endYaw = tYaw; + endPitch = MathHelper.clamp(targetPitch, -90f, 90f); + + float yawDelta = tYaw - curYaw; + float pitchDelta = endPitch - curPitch; + float angleMag = Math.abs(yawDelta) + Math.abs(pitchDelta); + + int baseTicks = (int) Math.max(1, Math.round(smoothAim.getRawState())); + double mult = Math.max(0.1, smoothingMultiplier.getRawState()); + planTicksTotal = (int) Math.max(baseTicks, Math.min(baseTicks * 4L, Math.round(baseTicks * mult * (0.5 + angleMag / 45f)))); + planTicksElapsed = 0; + + float c1t = 0.30f + (float) rng.nextDouble() * 0.15f; + float c2t = 0.70f - (float) rng.nextDouble() * 0.15f; + float ctrlJitter = (float) (Math.max(0.0f, Math.min(3.5f, angleMag * 0.05f)) * Math.max(0.0, controlJitterScale.getRawState())); + c1Yaw = curYaw + yawDelta * c1t + (float) (rng.nextGaussian() * ctrlJitter); + c2Yaw = curYaw + yawDelta * c2t + (float) (rng.nextGaussian() * ctrlJitter); + c1Pitch = curPitch + pitchDelta * c1t + (float) (rng.nextGaussian() * ctrlJitter * 0.6f); + c2Pitch = curPitch + pitchDelta * c2t + (float) (rng.nextGaussian() * ctrlJitter * 0.6f); + + c1Yaw = unwrapTowards(c1Yaw, startYaw); + c2Yaw = unwrapTowards(c2Yaw, startYaw); + endYaw = unwrapTowards(endYaw, startYaw); } - public static LivingEntity getBestEntityTargetFor(ClientPlayerEntity player) { - ClientWorld world = (ClientWorld) player.getWorld(); - Vec3d eyePos = player.getEyePos(); - double maxRange = 6.0; - Box search = player.getBoundingBox().expand(maxRange + 1.0); - - List players = WorldL.getRealPlayers(); - List villagers = world.getEntitiesByClass(VillagerEntity.class, search, - v -> v.isAlive() && v.distanceTo(player) <= maxRange); - List traders = world.getEntitiesByClass(WanderingTraderEntity.class, search, - t -> t.isAlive() && t.distanceTo(player) <= maxRange); - List zombieVillagers = world.getEntitiesByClass(ZombieVillagerEntity.class, search, - z -> z.isAlive() && z.distanceTo(player) <= maxRange); - - List villagerLike = new ArrayList<>(); - villagerLike.addAll(villagers); - villagerLike.addAll(traders); - villagerLike.addAll(zombieVillagers); - - if (!villagerLike.isEmpty()) { - villagerLike.sort(Comparator.comparingDouble(e -> eyePos.squaredDistanceTo(e.getEyePos()))); - return villagerLike.get(0); + private float applyInterpolation(float t) { + int mode = (int) Math.round(interpolationMode.getRawState()); + switch (mode) { + case 0: // Linear + return t; + case 1: // EaseInOutCubic + return easeInOutCubic(t); + case 2: // Cosine + return (float) (0.5 - 0.5 * Math.cos(Math.PI * t)); + case 3: // Smoothstep + return t * t * (3f - 2f * t); + default: + return t; } + } + + private boolean isPlanActive() { return planTicksElapsed < planTicksTotal; } + + private boolean planTargetChangedSignificantly(float[] targetAngles) { + float dyaw = Math.abs(MathHelper.wrapDegrees(endYaw - targetAngles[0])); + float dpitch = Math.abs(endPitch - targetAngles[1]); + return dyaw > 3.0f || dpitch > 3.0f; + } - List candidates = new ArrayList<>(); - for (AbstractClientPlayerEntity p : players) { - if (p == player) continue; - if (player.isTeammate(p) || ServerL.playerOnSameTeam(player, p)) continue; - if (p.isDead() || p.getHealth() <= 0) continue; - if (p.isInvulnerable()) continue; - if (p.age < 20) continue; - if (p.distanceTo(player) > maxRange) continue; - if (!player.canSee(p)) continue; - candidates.add(p); + private void clearPlan() { + activeTarget = null; + activeTargetId = null; + planTicksTotal = 0; + planTicksElapsed = 0; + } + + private static float cubicBezier(float p0, float p1, float p2, float p3, float t) { + float u = 1f - t; + return u*u*u*p0 + 3*u*u*t*p1 + 3*u*t*t*p2 + t*t*t*p3; + } + + private static float easeInOutCubic(float t) { + return t < 0.5f ? 4f * t * t * t : 1f - (float) Math.pow(-2f * t + 2f, 3f) / 2f; + } + + private static float unwrapTowards(float target, float reference) { + float t = target; + while (t - reference > 180f) t -= 360f; + while (t - reference < -180f) t += 360f; + return t; + } + + private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, boolean attackNow) { + return AimingL.getAimPointInsideHitbox(player, target, attackNow, 0.1, 0.6, 2.5); + } + + private @Nullable LivingEntity pickOrValidateTarget(ClientWorld world, ClientPlayerEntity player) { + if (activeTarget != null && activeTarget.isAlive() && !activeTarget.isRemoved()) { + if (activeTarget.squaredDistanceTo(player) <= (MELEE_REACH + 4.0) * (MELEE_REACH + 4.0)) { + if (!(activeTarget instanceof AbstractClientPlayerEntity p) || !ignoreTeammates.getRawState() || !ServerL.playerOnSameTeam(player, p)) { + return activeTarget; + } + } + } + + Stream players = world.getPlayers().stream() + .filter(p -> p != player) + .filter(p -> p.isAlive() && !p.isRemoved() && !p.isSpectator()) + .filter(p -> !ignoreTeammates.getRawState() || !ServerL.playerOnSameTeam(player, p)) + .map(p -> (LivingEntity) p); + + Stream villagers = Stream.empty(); + if (testMode.getRawState()) { + List vs = world.getEntitiesByClass(VillagerEntity.class, player.getBoundingBox().expand(12.0), v -> v.isAlive() && !v.isRemoved()); + villagers = vs.stream().map(v -> (LivingEntity) v); } - if (candidates.isEmpty()) return null; - candidates.sort(Comparator.comparingDouble(e -> eyePos.squaredDistanceTo(e.getEyePos()))); - return candidates.get(0); + + float curYaw = player.getYaw(); + return Stream.concat(players, villagers) + .sorted(Comparator.comparingDouble(player::squaredDistanceTo)) + .filter(e -> player.squaredDistanceTo(e) <= Math.pow(aimRange.getRawState(), 2)) + .min(Comparator.comparingDouble(e -> scoreTarget(player, curYaw, e))) + .orElse(null); + } + + private double scoreTarget(ClientPlayerEntity player, float curYaw, LivingEntity e) { + double dist = player.distanceTo(e); + double dx = e.getX() - player.getX(); + double dz = e.getZ() - player.getZ(); + double targetYaw = Math.toDegrees(Math.atan2(-dx, dz)); + double ang = Math.abs(MathHelper.wrapDegrees((float) (curYaw - targetYaw))); + double behindPenalty = ang > 120 ? 1000 : 0; + return dist + ang * 0.02 + behindPenalty; } + public static @Nullable AbstractClientPlayerEntity getBestTargetFor(ClientPlayerEntity self) { + if (self == null || self.clientWorld == null) return null; + ClientWorld world = self.clientWorld; + float curYaw = self.getYaw(); + return world.getPlayers().stream() + .filter(p -> p != self) + .filter(p -> p.isAlive() && !p.isRemoved() && !p.isSpectator()) + .filter(p -> !INSTANCE.ignoreTeammates.getRawState() || !ServerL.playerOnSameTeam(self, p)) + .min(Comparator.comparingDouble(p -> INSTANCE.scoreTarget(self, curYaw, p))) + .orElse(null); + } + @Override + protected void onDisabledTick(MinecraftClient client) { + clearPlan(); + } @Override public boolean inValidGame() { From 72f2e37281d67e51b3aecaa5fa481ef6b0f5f6e6 Mon Sep 17 00:00:00 2001 From: AH Date: Sun, 31 Aug 2025 13:13:58 -0400 Subject: [PATCH 11/23] fix: rotations no longer jump so we are not flagging grimac/matrix anymore, applied some fixes as mixins --- src/main/java/dev/cigarette/lib/AimingL.java | 19 ++++++++++++++++--- .../java/dev/cigarette/lib/PlayerEntityL.java | 11 ++++++----- .../cigarette/module/bedwars/AutoBlockIn.java | 6 ++++-- .../dev/cigarette/module/bedwars/Bridger.java | 18 +++++++++++++----- .../cigarette/module/combat/PlayerAimbot.java | 19 ++++++++++++------- 5 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/main/java/dev/cigarette/lib/AimingL.java b/src/main/java/dev/cigarette/lib/AimingL.java index 956251be..b1be8614 100644 --- a/src/main/java/dev/cigarette/lib/AimingL.java +++ b/src/main/java/dev/cigarette/lib/AimingL.java @@ -2,12 +2,14 @@ import dev.cigarette.helper.WeaponHelper; import dev.cigarette.mixin.ClientWorldAccessor; +import dev.cigarette.mixin.KeyBindingAccessor; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.PendingUpdateManager; import net.minecraft.client.option.KeyBinding; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; +import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.HitResult; import net.minecraft.world.RaycastContext; import net.minecraft.entity.LivingEntity; @@ -179,7 +181,9 @@ public static void switchToBestPvPWeapon(ClientPlayerEntity player, double dista */ public static void sendAimPacket(ClientWorld world, ClientPlayerEntity player, float yaw, float pitch) { if (world == null || player == null) return; - float syaw = sanitizeYaw(yaw); + // Keep yaw continuous relative to current player yaw to avoid modulo-360 jumps + float cur = player.getYaw(); + float syaw = Float.isFinite(yaw) ? (cur + MathHelper.wrapDegrees(yaw - cur)) : cur; float spitch = sanitizePitch(pitch); ClientWorldAccessor accessor = (ClientWorldAccessor) world; try (PendingUpdateManager pum = accessor.getPendingUpdateManager().incrementSequence()) { @@ -193,8 +197,17 @@ public static void sendAimPacket(ClientWorld world, ClientPlayerEntity player, f */ public static void lookAndAttack(ClientWorld world, ClientPlayerEntity player, LivingEntity target, float yaw, float pitch) { if (player == null || target == null || world == null) return; - player.attack(target); - player.swingHand(Hand.MAIN_HAND); + HitResult hitResult = MinecraftClient.getInstance().crosshairTarget; + KeyBinding attackKey = KeyBinding.byId("key.attack"); + KeyBindingAccessor attackKeyAccessor = (KeyBindingAccessor) attackKey; + if (hitResult == null || attackKey == null || !attackKey.isPressed()) return; + if (hitResult.getType() == HitResult.Type.ENTITY) { + EntityHitResult entityHitResult = (EntityHitResult) hitResult; + Entity entity = entityHitResult.getEntity(); + if (!(entity instanceof LivingEntity livingEntity)) return; + if (livingEntity.hurtTime > 1) return; + attackKeyAccessor.setTimesPressed(attackKeyAccessor.getTimesPressed() + 1); + } } // Helpers diff --git a/src/main/java/dev/cigarette/lib/PlayerEntityL.java b/src/main/java/dev/cigarette/lib/PlayerEntityL.java index 24c219b2..bdf562d7 100644 --- a/src/main/java/dev/cigarette/lib/PlayerEntityL.java +++ b/src/main/java/dev/cigarette/lib/PlayerEntityL.java @@ -4,6 +4,7 @@ import net.minecraft.item.BowItem; import net.minecraft.item.ItemStack; import net.minecraft.registry.tag.ItemTags; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; public class PlayerEntityL { @@ -26,11 +27,11 @@ public static float[] getRotationVectorInDirection(Vec3d vector) { */ public static void setRotationVector(PlayerEntity player, Vec3d vector) { float[] yawPitch = getRotationVectorInDirection(vector); - player.setYaw(yawPitch[0]); - player.setPitch(yawPitch[1]); + float currentYaw = player.getYaw(); + float continuousYaw = currentYaw + MathHelper.wrapDegrees(yawPitch[0] - currentYaw); + player.setYaw(continuousYaw); + float clampedPitch = MathHelper.clamp(yawPitch[1], -90f, 90f); + player.setPitch(clampedPitch); } -// public static float getDistance(PlayerEntity player, PlayerEntity other) { -// return (float) player.getPos().distanceTo(other.getPos()); -// } } diff --git a/src/main/java/dev/cigarette/module/bedwars/AutoBlockIn.java b/src/main/java/dev/cigarette/module/bedwars/AutoBlockIn.java index ee538958..8a9f81d4 100644 --- a/src/main/java/dev/cigarette/module/bedwars/AutoBlockIn.java +++ b/src/main/java/dev/cigarette/module/bedwars/AutoBlockIn.java @@ -59,8 +59,10 @@ private void enable(@NotNull ClientPlayerEntity player) { private void disable(@NotNull ClientPlayerEntity player) { running = false; - player.setYaw(originalYaw); - player.setPitch(originalPitch); + float curYaw = player.getYaw(); + float smoothYaw = curYaw + net.minecraft.util.math.MathHelper.wrapDegrees(originalYaw - curYaw); + player.setYaw(smoothYaw); + player.setPitch(net.minecraft.util.math.MathHelper.clamp(originalPitch, -90f, 90f)); previousVector = null; } diff --git a/src/main/java/dev/cigarette/module/bedwars/Bridger.java b/src/main/java/dev/cigarette/module/bedwars/Bridger.java index 6d3415b8..440b2a96 100644 --- a/src/main/java/dev/cigarette/module/bedwars/Bridger.java +++ b/src/main/java/dev/cigarette/module/bedwars/Bridger.java @@ -16,6 +16,7 @@ import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.MathHelper; import org.jetbrains.annotations.NotNull; public class Bridger extends TickModule { @@ -124,11 +125,17 @@ private void rightClick(int times) { useAccessor.setTimesPressed(useAccessor.getTimesPressed() + times); } - private void enable(BridgeType type, float yaw) { + private float continuousYawFor(ClientPlayerEntity player, float desiredYaw) { + float current = player.getYaw(); + float delta = MathHelper.wrapDegrees(desiredYaw - current); + return current + delta; + } + + private void enable(BridgeType type, ClientPlayerEntity player, float yaw) { this.bridgeType = type; InputOverride.isActive = true; InputOverride.pitch = 77.0f; - InputOverride.yaw = yaw; + InputOverride.yaw = continuousYawFor(player, yaw); InputOverride.sneakKey = true; InputOverride.backKey = true; InputOverride.leftKey = leftKey.isPressed(); @@ -169,11 +176,11 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, if (isStraightBridge()) { if (!toggleStraight.getRawState()) return; - enable(BridgeType.STRAIGHT, straightBridgeYaw(placingFace)); + enable(BridgeType.STRAIGHT, player, straightBridgeYaw(placingFace)); } else if (isDiagonalBridge()) { if (!toggleDiagonal.getRawState()) return; boolean godBridge = toggleDiagonalGod.getRawState() && isDiagonalGodBridge(playerPos, blockPos); - enable(godBridge ? BridgeType.DIAGONAL_GOD : BridgeType.DIAGONAL, diagonalBridgeYaw(player)); + enable(godBridge ? BridgeType.DIAGONAL_GOD : BridgeType.DIAGONAL, player, diagonalBridgeYaw(player)); } } else { if (!sneakKey.isPressed() || !backwardsKey.isPressed() || !rightClickKey.isPressed()) { @@ -263,4 +270,5 @@ public int getId() { } */ } -} \ No newline at end of file +} + diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index 9c594ec6..6c0885b9 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -108,7 +108,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, Vec3d aimPoint = computeAimPoint(player, target, false); float[] targetAngles = AimingL.anglesFromTo(player.getEyePos(), aimPoint); - float curYaw = MathHelper.wrapDegrees(player.getYaw()); + float curYaw = getContinuousYaw(player); float curPitch = MathHelper.clamp(player.getPitch(), -90f, 90f); if (!isPlanActive() || !Objects.equals(target.getUuid(), activeTargetId) || planTargetChangedSignificantly(targetAngles)) { @@ -120,8 +120,8 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, float[] stepAngles = evalPlanStep(); player.setYaw(stepAngles[0]); player.setPitch(stepAngles[1]); - lastAppliedYaw = player.getYaw(); - lastAppliedPitch = player.getPitch(); + lastAppliedYaw = stepAngles[0]; + lastAppliedPitch = stepAngles[1]; lastAppliedValid = true; if (autoAttack.getRawState()) { @@ -139,9 +139,9 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, } private boolean handleInterferenceSuppression(ClientPlayerEntity player) { - float yaw = player.getYaw(); + float yaw = getContinuousYaw(player); float pitch = player.getPitch(); - float angleDelta = Math.max(Math.abs(MathHelper.wrapDegrees(yaw - lastAppliedYaw)), Math.abs(pitch - lastAppliedPitch)); + float angleDelta = Math.max(Math.abs(yaw - lastAppliedYaw), Math.abs(pitch - lastAppliedPitch)); float trigger = (float) Math.max(0.5, interferenceAngleDeg.getRawState()); if (suppressTicks > 0) { @@ -203,7 +203,7 @@ private float[] evalPlanStep() { float driftYaw = (float) (rng.nextGaussian() * driftMag * 0.2); float driftPitch = (float) (rng.nextGaussian() * driftMag * 0.2); - float outYaw = MathHelper.wrapDegrees(yaw + fineJitterYaw + driftYaw); + float outYaw = yaw + fineJitterYaw + driftYaw; float outPitch = MathHelper.clamp(pitch + fineJitterPitch + driftPitch, -90f, 90f); return new float[]{outYaw, outPitch}; } @@ -284,6 +284,11 @@ private static float unwrapTowards(float target, float reference) { return t; } + private float getContinuousYaw(ClientPlayerEntity player) { + float wrappedCurrent = MathHelper.wrapDegrees(player.getYaw()); + return lastAppliedValid ? unwrapTowards(wrappedCurrent, lastAppliedYaw) : wrappedCurrent; + } + private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, boolean attackNow) { return AimingL.getAimPointInsideHitbox(player, target, attackNow, 0.1, 0.6, 2.5); } @@ -309,7 +314,7 @@ private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, bo villagers = vs.stream().map(v -> (LivingEntity) v); } - float curYaw = player.getYaw(); + float curYaw = getContinuousYaw(player); return Stream.concat(players, villagers) .sorted(Comparator.comparingDouble(player::squaredDistanceTo)) .filter(e -> player.squaredDistanceTo(e) <= Math.pow(aimRange.getRawState(), 2)) From 0325c2f229e4aab0765f6b067cd8693b97cdce67 Mon Sep 17 00:00:00 2001 From: AH Date: Mon, 1 Sep 2025 12:55:59 -0400 Subject: [PATCH 12/23] feat: mm aimbot --- .../java/dev/cigarette/mixin/EntityMixin.java | 1 - .../cigarette/module/combat/PlayerAimbot.java | 133 +++++++++++++++++- 2 files changed, 126 insertions(+), 8 deletions(-) diff --git a/src/main/java/dev/cigarette/mixin/EntityMixin.java b/src/main/java/dev/cigarette/mixin/EntityMixin.java index 21716af1..49bea5dd 100644 --- a/src/main/java/dev/cigarette/mixin/EntityMixin.java +++ b/src/main/java/dev/cigarette/mixin/EntityMixin.java @@ -39,7 +39,6 @@ public void setYaw(float yaw, CallbackInfo ci) { if (yaw > 360) yaw = yaw % 360; if (yaw < 0) yaw = 360 + (yaw % 360); } - @Inject(method = "setPitch", at = @At("HEAD")) public void setPitch(float pitch, CallbackInfo ci) { if (pitch > 90) pitch = 90; diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index 6c0885b9..fd06f1d9 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -1,8 +1,10 @@ package dev.cigarette.module.combat; import dev.cigarette.GameDetector; +import dev.cigarette.agent.MurderMysteryAgent; import dev.cigarette.gui.widget.SliderWidget; import dev.cigarette.gui.widget.ToggleWidget; +import dev.cigarette.gui.widget.ToggleKeybindWidget; import dev.cigarette.lib.AimingL; import dev.cigarette.lib.ServerL; import dev.cigarette.module.TickModule; @@ -25,7 +27,7 @@ import java.util.stream.Stream; public class PlayerAimbot extends TickModule { - public static final PlayerAimbot INSTANCE = new PlayerAimbot("combat.playerAimbot", "PlayerAimbot", "Automatically aims at players."); + public static final PlayerAimbot INSTANCE = new PlayerAimbot("combat.playerAimbot", "PlayerAimbot", "Automatically aims at players. The legitest killaura."); private final ToggleWidget autoAttack = new ToggleWidget("Auto Attack", "Automatically hits players").withDefaultState(true); public final SliderWidget smoothAim = new SliderWidget("Smooth Aim", "How quickly to snap to target in ticks").withBounds(1, 5, 20); @@ -36,6 +38,13 @@ public class PlayerAimbot extends TickModule { public final ToggleWidget ignoreTeammates = new ToggleWidget("Ignore Teammates", "Don't target players on your team").withDefaultState(true); + private final ToggleKeybindWidget lockOnKeybind = new ToggleKeybindWidget( + "Lock-On Trigger", "Press key to lock to best target. Releases only when target dies" + ).withDefaultState(false); + + private final ToggleWidget murderMysteryMode = new ToggleWidget("Murder Mystery", "Prefer murderer and detective targets").withDefaultState(false); + private final ToggleWidget detectiveAim = new ToggleWidget("Detective Aim", "Aim at a detective when no Murderer is available (or you are the mur)").withDefaultState(true); + public final SliderWidget jitterViolence = new SliderWidget("Jitter Aggression", "Scale of aim jitter (0 disables)").withBounds(0.0, 1.0, 3.0).withAccuracy(2); public final SliderWidget driftViolence = new SliderWidget("Drift Aggression", "Scale of slow drift vs jitter").withBounds(0.0, 0.6, 2.0).withAccuracy(2); public final SliderWidget aimToleranceDeg = new SliderWidget("Aim Tolerance (deg)", "Max degrees off for attack").withBounds(0.5, 2.0, 6.0).withAccuracy(1); @@ -52,7 +61,8 @@ public class PlayerAimbot extends TickModule { private PlayerAimbot(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(autoAttack, smoothAim, aimRange, wTap, attackCps, ignoreTeammates, testMode, + this.setChildren(autoAttack, smoothAim, aimRange, wTap, attackCps, ignoreTeammates, lockOnKeybind, testMode, + murderMysteryMode, detectiveAim, jitterViolence, driftViolence, aimToleranceDeg, smoothingMultiplier, bezierInfluence, controlJitterScale, interpolationMode, interferenceAngleDeg, interferenceGraceTicks); autoAttack.registerConfigKey(id + ".autoAttack"); @@ -61,7 +71,10 @@ private PlayerAimbot(String id, String name, String tooltip) { wTap.registerConfigKey(id + ".wTap"); attackCps.registerConfigKey(id + ".attackCps"); ignoreTeammates.registerConfigKey(id + ".ignoreTeammates"); + lockOnKeybind.registerConfigKey(id + ".lockOnKeybind"); testMode.registerConfigKey(id + ".testMode"); + murderMysteryMode.registerConfigKey(id + ".murderMysteryMode"); + detectiveAim.registerConfigKey(id + ".detectiveAim"); jitterViolence.registerConfigKey(id + ".jitterViolence"); driftViolence.registerConfigKey(id + ".driftViolence"); aimToleranceDeg.registerConfigKey(id + ".aimToleranceDeg"); @@ -94,13 +107,60 @@ private PlayerAimbot(String id, String name, String tooltip) { private boolean lastAppliedValid = false; private float lastAppliedYaw = 0f, lastAppliedPitch = 0f; + private boolean lockOnEngaged = false; + @Override protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { if (MinecraftClient.getInstance().currentScreen != null) return; - if (handleInterferenceSuppression(player)) return; + if (!lockOnKeybind.getRawState() && lockOnEngaged) { + lockOnEngaged = false; + clearPlan(); + } + boolean keyPressedEvent = lockOnKeybind.getKeybind().wasPressed(); + boolean keyHeld = lockOnKeybind.getKeybind().isPressed(); + LivingEntity target; + if (keyPressedEvent || keyHeld) { + if (lockOnKeybind.getRawState() && lockOnKeybind.getKeybind().wasPressed()) { + AbstractClientPlayerEntity best = getBestTargetFor(player); + if (best != null) { + lockOnEngaged = true; + activeTarget = best; + activeTargetId = best.getUuid(); + planTicksTotal = 0; + planTicksElapsed = 0; + } + } + + target = null; + if (handleInterferenceSuppression(player)) return; + + if (lockOnKeybind.getRawState()) { + if (lockOnEngaged) { + if (activeTarget != null && activeTarget.isAlive() && !activeTarget.isRemoved() + && player.squaredDistanceTo(activeTarget) <= Math.pow(aimRange.getRawState(), 2)) { + target = activeTarget; + } else { + lockOnEngaged = false; + clearPlan(); + return; + } + } else { + clearPlan(); + return; + } + } else { + target = pickOrValidateTarget(world, player); + } + + if (target == null) { + clearPlan(); + return; + } + } else { + target = pickOrValidateTarget(world, player); + } - LivingEntity target = pickOrValidateTarget(world, player); if (target == null) { clearPlan(); return; @@ -293,10 +353,56 @@ private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, bo return AimingL.getAimPointInsideHitbox(player, target, attackNow, 0.1, 0.6, 2.5); } + private boolean isMurderMysteryActive() { + return murderMysteryMode.getRawState(); + } + + private boolean shouldFilterTeammates() { + return !isMurderMysteryActive() && ignoreTeammates.getRawState(); + } + + private @Nullable AbstractClientPlayerEntity detectMurderer(@NotNull ClientPlayerEntity self) { + if (!isMurderMysteryActive()) return null; + float curYaw = getContinuousYaw(self); + return MurderMysteryAgent.getVisiblePlayers().stream() + .filter(pp -> pp != null && pp.playerEntity != null && pp.playerEntity.isAlive()) + .filter(pp -> pp.role == MurderMysteryAgent.PersistentPlayer.Role.MURDERER) + .map(pp -> pp.playerEntity) + .filter(pe -> pe != self) + .filter(pe -> self.squaredDistanceTo(pe) <= Math.pow(aimRange.getRawState(), 2)) + .filter(pe -> pe instanceof AbstractClientPlayerEntity) + .map(pe -> (AbstractClientPlayerEntity) pe) + .min(Comparator.comparingDouble(p -> scoreTarget(self, curYaw, p))) + .orElse(null); + } + + private @Nullable AbstractClientPlayerEntity detectDetective(@NotNull ClientPlayerEntity self) { + if (!isMurderMysteryActive() || !detectiveAim.getRawState()) return null; + float curYaw = getContinuousYaw(self); + return MurderMysteryAgent.getVisiblePlayers().stream() + .filter(pp -> pp != null && pp.playerEntity != null && pp.playerEntity.isAlive()) + .filter(pp -> pp.role == MurderMysteryAgent.PersistentPlayer.Role.DETECTIVE) + .map(pp -> pp.playerEntity) + .filter(pe -> pe != self) + .filter(pe -> self.squaredDistanceTo(pe) <= Math.pow(aimRange.getRawState(), 2)) + .filter(pe -> pe instanceof AbstractClientPlayerEntity) + .map(pe -> (AbstractClientPlayerEntity) pe) + .min(Comparator.comparingDouble(p -> scoreTarget(self, curYaw, p))) + .orElse(null); + } + private @Nullable LivingEntity pickOrValidateTarget(ClientWorld world, ClientPlayerEntity player) { + if (isMurderMysteryActive()) { + AbstractClientPlayerEntity mm = detectMurderer(player); + if (mm != null) return mm; + AbstractClientPlayerEntity det = detectDetective(player); + if (det != null) return det; + return null; + } + if (activeTarget != null && activeTarget.isAlive() && !activeTarget.isRemoved()) { if (activeTarget.squaredDistanceTo(player) <= (MELEE_REACH + 4.0) * (MELEE_REACH + 4.0)) { - if (!(activeTarget instanceof AbstractClientPlayerEntity p) || !ignoreTeammates.getRawState() || !ServerL.playerOnSameTeam(player, p)) { + if (!(activeTarget instanceof AbstractClientPlayerEntity p) || !shouldFilterTeammates() || !ServerL.playerOnSameTeam(player, p)) { return activeTarget; } } @@ -305,7 +411,7 @@ private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, bo Stream players = world.getPlayers().stream() .filter(p -> p != player) .filter(p -> p.isAlive() && !p.isRemoved() && !p.isSpectator()) - .filter(p -> !ignoreTeammates.getRawState() || !ServerL.playerOnSameTeam(player, p)) + .filter(p -> !shouldFilterTeammates() || !ServerL.playerOnSameTeam(player, p)) .map(p -> (LivingEntity) p); Stream villagers = Stream.empty(); @@ -336,10 +442,22 @@ private double scoreTarget(ClientPlayerEntity player, float curYaw, LivingEntity if (self == null || self.clientWorld == null) return null; ClientWorld world = self.clientWorld; float curYaw = self.getYaw(); + double range2 = Math.pow(INSTANCE.aimRange.getRawState(), 2); + + if (INSTANCE.isMurderMysteryActive()) { + AbstractClientPlayerEntity mm = INSTANCE.detectMurderer(self); + if (mm != null) return mm; + AbstractClientPlayerEntity det = INSTANCE.detectDetective(self); + if (det != null) return det; + return null; + } + + boolean filterTeams = !INSTANCE.isMurderMysteryActive() && INSTANCE.ignoreTeammates.getRawState(); return world.getPlayers().stream() .filter(p -> p != self) .filter(p -> p.isAlive() && !p.isRemoved() && !p.isSpectator()) - .filter(p -> !INSTANCE.ignoreTeammates.getRawState() || !ServerL.playerOnSameTeam(self, p)) + .filter(p -> self.squaredDistanceTo(p) <= range2) + .filter(p -> !filterTeams || !ServerL.playerOnSameTeam(self, p)) .min(Comparator.comparingDouble(p -> INSTANCE.scoreTarget(self, curYaw, p))) .orElse(null); } @@ -347,6 +465,7 @@ private double scoreTarget(ClientPlayerEntity player, float curYaw, LivingEntity @Override protected void onDisabledTick(MinecraftClient client) { clearPlan(); + lockOnEngaged = false; } @Override From 448a9f66c63e0741c9fff28337a9e60fa480b94f Mon Sep 17 00:00:00 2001 From: AH Date: Wed, 3 Sep 2025 18:38:44 -0400 Subject: [PATCH 13/23] Add AutoBow --- .../cigarette/agent/MurderMysteryAgent.java | 13 +- .../cigarette/mixin/KeyBindingAccessor.java | 2 + .../cigarette/module/combat/PlayerAimbot.java | 6 +- .../module/murdermystery/AutoBow.java | 116 ++++++++++++++++++ 4 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 src/main/java/dev/cigarette/module/murdermystery/AutoBow.java diff --git a/src/main/java/dev/cigarette/agent/MurderMysteryAgent.java b/src/main/java/dev/cigarette/agent/MurderMysteryAgent.java index 5cbe148f..c01c573b 100644 --- a/src/main/java/dev/cigarette/agent/MurderMysteryAgent.java +++ b/src/main/java/dev/cigarette/agent/MurderMysteryAgent.java @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.HashSet; +import java.util.Optional; import java.util.UUID; public class MurderMysteryAgent extends BaseAgent { @@ -53,10 +54,9 @@ private void cleanupAvailableGold() { } } - private boolean isDetectiveItem(ItemStack item) { + public static boolean isDetectiveItem(ItemStack item) { if (item.isOf(Items.ARROW)) return true; - if (item.isOf(Items.BOW)) return true; - return false; + return item.isOf(Items.BOW); } private PersistentPlayer getOrCreatePersistentPlayer(PlayerEntity player) { @@ -104,6 +104,7 @@ protected void onValidTick(MinecraftClient client, @NotNull ClientWorld world, @ if (item == ItemStack.EMPTY) continue; if (this.isDetectiveItem(item)) { existingPlayer.setDetective(); + existingPlayer.setItemStack(item); continue; } @@ -113,6 +114,7 @@ protected void onValidTick(MinecraftClient client, @NotNull ClientWorld world, @ for (String knife : knives) { if (knife.equals(knifeLang)) { existingPlayer.setMurderer(); + existingPlayer.setItemStack(item); break; } } @@ -139,6 +141,7 @@ public static class PersistentPlayer { public PlayerEntity playerEntity; public final String name; public Role role; + public ItemStack itemStack; public PersistentPlayer(PlayerEntity playerEntity) { this.playerEntity = playerEntity; @@ -146,6 +149,10 @@ public PersistentPlayer(PlayerEntity playerEntity) { this.role = Role.INNOCENT; } + public void setItemStack(ItemStack itemStack) { + this.itemStack = itemStack; + } + protected void setPlayerEntity(PlayerEntity playerEntity) { this.playerEntity = playerEntity; } diff --git a/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java b/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java index 726af328..8075e8a1 100644 --- a/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java +++ b/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java @@ -9,6 +9,8 @@ public interface KeyBindingAccessor { @Accessor("timesPressed") public void setTimesPressed(int timesPressed); + @Accessor("") + @Accessor int getTimesPressed(); } diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index fd06f1d9..6326ad9b 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -42,8 +42,8 @@ public class PlayerAimbot extends TickModule { "Lock-On Trigger", "Press key to lock to best target. Releases only when target dies" ).withDefaultState(false); - private final ToggleWidget murderMysteryMode = new ToggleWidget("Murder Mystery", "Prefer murderer and detective targets").withDefaultState(false); - private final ToggleWidget detectiveAim = new ToggleWidget("Detective Aim", "Aim at a detective when no Murderer is available (or you are the mur)").withDefaultState(true); + public final ToggleWidget murderMysteryMode = new ToggleWidget("Murder Mystery", "Prefer murderer and detective targets").withDefaultState(false); + public final ToggleWidget detectiveAim = new ToggleWidget("Detective Aim", "Aim at a detective when no Murderer is available (or you are the mur)").withDefaultState(true); public final SliderWidget jitterViolence = new SliderWidget("Jitter Aggression", "Scale of aim jitter (0 disables)").withBounds(0.0, 1.0, 3.0).withAccuracy(2); public final SliderWidget driftViolence = new SliderWidget("Drift Aggression", "Scale of slow drift vs jitter").withBounds(0.0, 0.6, 2.0).withAccuracy(2); @@ -87,7 +87,7 @@ private PlayerAimbot(String id, String name, String tooltip) { } // Bezier aim plan state - private @Nullable LivingEntity activeTarget = null; + public @Nullable LivingEntity activeTarget = null; private @Nullable UUID activeTargetId = null; private int planTicksTotal = 0; private int planTicksElapsed = 0; diff --git a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java new file mode 100644 index 00000000..8e26b8de --- /dev/null +++ b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java @@ -0,0 +1,116 @@ +package dev.cigarette.module.murdermystery; + +import dev.cigarette.agent.MurderMysteryAgent; +import dev.cigarette.gui.widget.SliderWidget; +import dev.cigarette.gui.widget.ToggleWidget; +import dev.cigarette.mixin.KeyBindingAccessor; +import dev.cigarette.module.TickModule; +import dev.cigarette.module.combat.AutoClicker; +import dev.cigarette.module.combat.PerfectHit; +import dev.cigarette.module.combat.PlayerAimbot; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.hit.EntityHitResult; +import net.minecraft.util.hit.HitResult; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Optional; + +public class AutoBow extends TickModule { + public static final dev.cigarette.module.murdermystery.AutoBow INSTANCE = new dev.cigarette.module.murdermystery.AutoBow("murdermystery.autobow", "AutoBow", "Automatically aims and fires a bow at the murderer."); + + private final SliderWidget shootDelay = new SliderWidget("Shoot Delay", "Maximum range to target players").withBounds(20, 45, 60).withAccuracy(1); + + private boolean paOldEnableState, paMMOldState = false; + + private boolean isMurderer = false; + + private int aimTicks = 0; + private int itemSlot; + + private AutoBow(String id, String name, String tooltip) { + super(ToggleWidget::module, id, name, tooltip); + this.setChildren(shootDelay); + shootDelay.registerConfigKey(id + ".shootDelay"); + } + + @Override + protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { + client.attackCooldown = 0; + HitResult hitResult = client.crosshairTarget; + if (hitResult.getType() == HitResult.Type.MISS) {} + KeyBinding aimKey = KeyBinding.byId("key.use"); + KeyBindingAccessor aimAccessor = (KeyBindingAccessor) aimKey; + if (hitResult == null || aimKey == null || aimKey.isPressed()) return; + + LivingEntity activeTarget = PlayerAimbot.INSTANCE.activeTarget; + if (activeTarget == null) return; + Optional tPlayer = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == activeTarget).findFirst(); + if (tPlayer.isPresent()) { + MurderMysteryAgent.PersistentPlayer.Role targetRole = tPlayer.get().role; + Optional self = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == MinecraftClient.getInstance().player).findFirst(); + if (self.isPresent()) { + if (self.get().role == MurderMysteryAgent.PersistentPlayer.Role.MURDERER) { + isMurderer = true; + ItemStack i = self.get().itemStack; + DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); + for (int ix = 0; ix < is.toArray().length; ix++) { + if (is.get(ix).equals(i)) { + assert MinecraftClient.getInstance().player != null; + MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); + this.itemSlot = ix; + } + } + } else { + isMurderer = false; + DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); + for (int ix = 0; ix < is.toArray().length; ix++) { + if (MurderMysteryAgent.isDetectiveItem(is.get(ix))) { + assert MinecraftClient.getInstance().player != null; + MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); + this.itemSlot = ix; + } + } + } + } + } + + if (this.aimTicks < this.shootDelay.getRawState().intValue()) { + assert MinecraftClient.getInstance().player != null; + if (MinecraftClient.getInstance().player.getInventory().getSelectedSlot() != this.itemSlot) { + aimKey.setPressed(false); + this.aimTicks = 0; + return; + } + aimKey.setPressed(true); + aimTicks++; + } else { + aimKey.setPressed(false); + this.aimTicks = 0; + } + } + + @Override + protected void whenEnabled() { + this.paOldEnableState = PlayerAimbot.INSTANCE.getRawState(); + this.paMMOldState = PlayerAimbot.INSTANCE.murderMysteryMode.getRawState(); + + AutoClicker.INSTANCE.widget.setRawState(false); + PlayerAimbot.INSTANCE.widget.setRawState(true); + PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(true); + } + + @Override + protected void whenDisabled() { + PlayerAimbot.INSTANCE.widget.setRawState(this.paOldEnableState); + PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(this.paMMOldState); + } +} From a07995d19621bb6b05decc729a1810dbc7f10117 Mon Sep 17 00:00:00 2001 From: AH Date: Wed, 3 Sep 2025 18:39:23 -0400 Subject: [PATCH 14/23] feat: AutoBow --- src/main/java/dev/cigarette/config/Config.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/dev/cigarette/config/Config.java b/src/main/java/dev/cigarette/config/Config.java index ad617da6..1a05967b 100644 --- a/src/main/java/dev/cigarette/config/Config.java +++ b/src/main/java/dev/cigarette/config/Config.java @@ -12,6 +12,7 @@ import dev.cigarette.module.keybind.AddGlassBlock; import dev.cigarette.module.keybind.BreakBlock; import dev.cigarette.module.keybind.VClip; +import dev.cigarette.module.murdermystery.AutoBow; import dev.cigarette.module.murdermystery.GoldESP; import dev.cigarette.module.murdermystery.PlayerESP; import dev.cigarette.module.render.ProjectileESP; @@ -71,7 +72,7 @@ public static Config construct() { cfg.putModules("Bedwars", AutoBlockIn.INSTANCE, AutoTool.INSTANCE, Bridger.INSTANCE, DefenseViewer.INSTANCE, EntityESP.INSTANCE, FireballESP.INSTANCE); cfg.putModules("Combat", AutoClicker.INSTANCE, JumpReset.INSTANCE, PerfectHit.INSTANCE, PlayerAimbot.INSTANCE); cfg.putModules("Keybind", AddGlassBlock.INSTANCE, BreakBlock.INSTANCE, VClip.INSTANCE); - cfg.putModules("Murder Mystery", GoldESP.INSTANCE, PlayerESP.INSTANCE); + cfg.putModules("Murder Mystery", GoldESP.INSTANCE, PlayerESP.INSTANCE, AutoBow.INSTANCE); cfg.putModules("Render", dev.cigarette.module.render.PlayerESP.INSTANCE, ProjectileESP.INSTANCE); cfg.putModules("UI", GUI.INSTANCE, ModuleList.INSTANCE, Notifications.INSTANCE, TargetHUD.INSTANCE, Watermark.INSTANCE); cfg.putModules("Zombies", Aimbot.INSTANCE, PowerupESP.INSTANCE, ReviveAura.INSTANCE, ZombieESP.INSTANCE); From b63510121d053c518a3c4b1ed5e3a2b020a2371e Mon Sep 17 00:00:00 2001 From: AH Date: Thu, 4 Sep 2025 10:24:34 -0400 Subject: [PATCH 15/23] fix: Ensure AutoBow's PlayerAimbot compliance & add generic mode --- .../cigarette/mixin/KeyBindingAccessor.java | 2 - .../module/murdermystery/AutoBow.java | 95 +++++++++++++------ src/main/resources/cigarette.accesswidener | 19 ---- src/main/resources/fabric.mod.json | 3 +- 4 files changed, 65 insertions(+), 54 deletions(-) delete mode 100644 src/main/resources/cigarette.accesswidener diff --git a/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java b/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java index 8075e8a1..726af328 100644 --- a/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java +++ b/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java @@ -9,8 +9,6 @@ public interface KeyBindingAccessor { @Accessor("timesPressed") public void setTimesPressed(int timesPressed); - @Accessor("") - @Accessor int getTimesPressed(); } diff --git a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java index 8e26b8de..492cc483 100644 --- a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java +++ b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java @@ -28,6 +28,7 @@ public class AutoBow extends TickModule { public static final dev.cigarette.module.murdermystery.AutoBow INSTANCE = new dev.cigarette.module.murdermystery.AutoBow("murdermystery.autobow", "AutoBow", "Automatically aims and fires a bow at the murderer."); private final SliderWidget shootDelay = new SliderWidget("Shoot Delay", "Maximum range to target players").withBounds(20, 45, 60).withAccuracy(1); + private final ToggleWidget genericMode = new ToggleWidget("Generic Mode", "Use PlayerAimbot target without role checks").withDefaultState(false); private boolean paOldEnableState, paMMOldState = false; @@ -38,8 +39,9 @@ public class AutoBow extends TickModule { private AutoBow(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(shootDelay); + this.setChildren(shootDelay, genericMode); shootDelay.registerConfigKey(id + ".shootDelay"); + genericMode.registerConfigKey(id + ".genericMode"); } @Override @@ -53,49 +55,78 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, LivingEntity activeTarget = PlayerAimbot.INSTANCE.activeTarget; if (activeTarget == null) return; - Optional tPlayer = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == activeTarget).findFirst(); - if (tPlayer.isPresent()) { - MurderMysteryAgent.PersistentPlayer.Role targetRole = tPlayer.get().role; - Optional self = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == MinecraftClient.getInstance().player).findFirst(); - if (self.isPresent()) { - if (self.get().role == MurderMysteryAgent.PersistentPlayer.Role.MURDERER) { - isMurderer = true; - ItemStack i = self.get().itemStack; - DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); - for (int ix = 0; ix < is.toArray().length; ix++) { - if (is.get(ix).equals(i)) { - assert MinecraftClient.getInstance().player != null; - MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); - this.itemSlot = ix; + + if (!genericMode.getRawState()) { + Optional tPlayer = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == activeTarget).findFirst(); + if (tPlayer.isPresent()) { + MurderMysteryAgent.PersistentPlayer.Role targetRole = tPlayer.get().role; + Optional self = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == MinecraftClient.getInstance().player).findFirst(); + if (self.isPresent()) { + if (self.get().role == MurderMysteryAgent.PersistentPlayer.Role.MURDERER) { + isMurderer = true; + ItemStack i = self.get().itemStack; + DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); + for (int ix = 0; ix < is.toArray().length; ix++) { + if (is.get(ix).equals(i)) { + assert MinecraftClient.getInstance().player != null; + MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); + this.itemSlot = ix; + } } - } - } else { - isMurderer = false; - DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); - for (int ix = 0; ix < is.toArray().length; ix++) { - if (MurderMysteryAgent.isDetectiveItem(is.get(ix))) { - assert MinecraftClient.getInstance().player != null; - MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); - this.itemSlot = ix; + } else { + isMurderer = false; + DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); + for (int ix = 0; ix < is.toArray().length; ix++) { + if (MurderMysteryAgent.isDetectiveItem(is.get(ix))) { + assert MinecraftClient.getInstance().player != null; + MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); + this.itemSlot = ix; + } } } } } } - if (this.aimTicks < this.shootDelay.getRawState().intValue()) { - assert MinecraftClient.getInstance().player != null; - if (MinecraftClient.getInstance().player.getInventory().getSelectedSlot() != this.itemSlot) { - aimKey.setPressed(false); - this.aimTicks = 0; - return; + boolean shouldAim = true; + if (this.aimTicks >= this.shootDelay.getRawState().intValue()) { + shouldAim = false; + } else if (genericMode.getRawState() && activeTarget != null && hitResult.getType() == HitResult.Type.ENTITY) { + EntityHitResult entityHit = (EntityHitResult) hitResult; + if (entityHit.getEntity() == activeTarget) { + shouldAim = false; } + } + + assert MinecraftClient.getInstance().player != null; + if (!genericMode.getRawState() && MinecraftClient.getInstance().player.getInventory().getSelectedSlot() != this.itemSlot) { + aimKey.setPressed(false); + this.aimTicks = 0; + return; + } + + if (shouldAim) { aimKey.setPressed(true); aimTicks++; } else { aimKey.setPressed(false); this.aimTicks = 0; } + + + // if (this.aimTicks < this.shootDelay.getRawState().intValue()) { + // assert MinecraftClient.getInstance().player != null; + // if (MinecraftClient.getInstance().player.getInventory().getSelectedSlot() != this.itemSlot) { + // aimKey.setPressed(false); + // this.aimTicks = 0; + // return; + // } + // aimKey.setPressed(true); + // aimTicks++; + // } else { + // aimKey.setPressed(false); + // this.aimTicks = 0; + // } } @Override @@ -105,7 +136,9 @@ protected void whenEnabled() { AutoClicker.INSTANCE.widget.setRawState(false); PlayerAimbot.INSTANCE.widget.setRawState(true); - PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(true); + if (!genericMode.getRawState()) { + PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(true); + } } @Override diff --git a/src/main/resources/cigarette.accesswidener b/src/main/resources/cigarette.accesswidener deleted file mode 100644 index b8bf59a8..00000000 --- a/src/main/resources/cigarette.accesswidener +++ /dev/null @@ -1,19 +0,0 @@ -accessWidener v1 named - -# USES https://raw.githubusercontent.com/CCBlueX/LiquidBounce/bee12584bfbc15c302803c7c40f9cdd797630244/src/main/resources/liquidbounce.accesswidener - -# This file is part of LiquidBounce (https://github.com/CCBlueX/LiquidBounce) -# -# Copyright (c) 2015 - 2025 CCBlueX -# -# LiquidBounce is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# LiquidBounce is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -accessible method net/minecraft/client/network/ClientPlayerInteractionManager sendSequencedPacket (Lnet/minecraft/client/world/ClientWorld;Lnet/minecraft/client/network/SequencedPacketCreator;)V \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 2f31ffa3..4e915f3b 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -30,6 +30,5 @@ "minecraft": "~1.21.5", "java": ">=21", "fabric-api": "*" - }, - "accessWidener": "cigarette.accesswidener" + } } \ No newline at end of file From de4133129a2fb31494b7260218c4f0e758c641d3 Mon Sep 17 00:00:00 2001 From: AH Date: Thu, 4 Sep 2025 10:36:21 -0400 Subject: [PATCH 16/23] feat(AutoBow): PlayerAimbot position prediction for player shooting time. --- .../cigarette/module/combat/PlayerAimbot.java | 15 +++++++++++++-- .../cigarette/module/murdermystery/AutoBow.java | 17 +++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index 6326ad9b..0576d93a 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -59,15 +59,20 @@ public class PlayerAimbot extends TickModule { public final SliderWidget aimRange = new SliderWidget("Aim Range", "Maximum range to target players").withBounds(3, 6, 20).withAccuracy(1); + public final ToggleWidget prediction = new ToggleWidget("Prediction", "Predict target position").withDefaultState(false); + public final SliderWidget predictionTicks = new SliderWidget("Prediction Ticks", "Ticks ahead to predict").withBounds(0, 5, 20).withAccuracy(1); + private PlayerAimbot(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(autoAttack, smoothAim, aimRange, wTap, attackCps, ignoreTeammates, lockOnKeybind, testMode, + this.setChildren(autoAttack, smoothAim, aimRange, prediction, predictionTicks, wTap, attackCps, ignoreTeammates, lockOnKeybind, testMode, murderMysteryMode, detectiveAim, jitterViolence, driftViolence, aimToleranceDeg, smoothingMultiplier, bezierInfluence, controlJitterScale, interpolationMode, interferenceAngleDeg, interferenceGraceTicks); autoAttack.registerConfigKey(id + ".autoAttack"); smoothAim.registerConfigKey(id + ".smoothAim"); aimRange.registerConfigKey(id + ".aimRange"); + prediction.registerConfigKey(id + ".prediction"); + predictionTicks.registerConfigKey(id + ".predictionTicks"); wTap.registerConfigKey(id + ".wTap"); attackCps.registerConfigKey(id + ".attackCps"); ignoreTeammates.registerConfigKey(id + ".ignoreTeammates"); @@ -350,7 +355,13 @@ private float getContinuousYaw(ClientPlayerEntity player) { } private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, boolean attackNow) { - return AimingL.getAimPointInsideHitbox(player, target, attackNow, 0.1, 0.6, 2.5); + if (prediction.getRawState()) { + Vec3d vel = target.getVelocity(); + Vec3d predictedPos = target.getPos().add(vel.multiply(predictionTicks.getRawState())); + return predictedPos.add(0, target.getHeight() * 0.5, 0); + } else { + return AimingL.getAimPointInsideHitbox(player, target, attackNow, 0.1, 0.6, 2.5); + } } private boolean isMurderMysteryActive() { diff --git a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java index 492cc483..28ee3de9 100644 --- a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java +++ b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java @@ -30,7 +30,12 @@ public class AutoBow extends TickModule { private final SliderWidget shootDelay = new SliderWidget("Shoot Delay", "Maximum range to target players").withBounds(20, 45, 60).withAccuracy(1); private final ToggleWidget genericMode = new ToggleWidget("Generic Mode", "Use PlayerAimbot target without role checks").withDefaultState(false); - private boolean paOldEnableState, paMMOldState = false; + private final ToggleWidget prediction = new ToggleWidget("Prediction", "Predict target position for bow").withDefaultState(false); + private final SliderWidget predictionTicks = new SliderWidget("Prediction Ticks", "Ticks ahead to predict").withBounds(0, 5, 20).withAccuracy(1); + + private boolean paOldEnableState, paOldPredictionState, paMMOldState = false; + + private double paOldPredictionTicks; private boolean isMurderer = false; @@ -39,9 +44,11 @@ public class AutoBow extends TickModule { private AutoBow(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(shootDelay, genericMode); + this.setChildren(shootDelay, genericMode, prediction, predictionTicks); shootDelay.registerConfigKey(id + ".shootDelay"); genericMode.registerConfigKey(id + ".genericMode"); + prediction.registerConfigKey(id + ".prediction"); + predictionTicks.registerConfigKey(id + ".predictionTicks"); } @Override @@ -133,9 +140,13 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, protected void whenEnabled() { this.paOldEnableState = PlayerAimbot.INSTANCE.getRawState(); this.paMMOldState = PlayerAimbot.INSTANCE.murderMysteryMode.getRawState(); + this.paOldPredictionState = PlayerAimbot.INSTANCE.prediction.getRawState(); + this.paOldPredictionTicks = PlayerAimbot.INSTANCE.predictionTicks.getRawState(); AutoClicker.INSTANCE.widget.setRawState(false); PlayerAimbot.INSTANCE.widget.setRawState(true); + PlayerAimbot.INSTANCE.prediction.setRawState(prediction.getRawState()); + PlayerAimbot.INSTANCE.predictionTicks.setRawState(predictionTicks.getRawState()); if (!genericMode.getRawState()) { PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(true); } @@ -145,5 +156,7 @@ protected void whenEnabled() { protected void whenDisabled() { PlayerAimbot.INSTANCE.widget.setRawState(this.paOldEnableState); PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(this.paMMOldState); + PlayerAimbot.INSTANCE.prediction.setRawState(this.paOldPredictionState); + PlayerAimbot.INSTANCE.predictionTicks.setRawState(this.paOldPredictionTicks); } } From 0db370cc11286f8ad91ad7f080f1168c343723bb Mon Sep 17 00:00:00 2001 From: AH Date: Fri, 5 Sep 2025 23:22:12 -0400 Subject: [PATCH 17/23] feat: Configurable draw duration and acceptable aim range --- .../cigarette/module/combat/PlayerAimbot.java | 6 +- .../module/murdermystery/AutoBow.java | 126 +++++++++++------- 2 files changed, 80 insertions(+), 52 deletions(-) diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index 0576d93a..f0b4b355 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -47,7 +47,7 @@ public class PlayerAimbot extends TickModule { public final SliderWidget jitterViolence = new SliderWidget("Jitter Aggression", "Scale of aim jitter (0 disables)").withBounds(0.0, 1.0, 3.0).withAccuracy(2); public final SliderWidget driftViolence = new SliderWidget("Drift Aggression", "Scale of slow drift vs jitter").withBounds(0.0, 0.6, 2.0).withAccuracy(2); - public final SliderWidget aimToleranceDeg = new SliderWidget("Aim Tolerance (deg)", "Max degrees off for attack").withBounds(0.5, 2.0, 6.0).withAccuracy(1); + public final SliderWidget aimToleranceDeg = new SliderWidget("Aim Tolerance (deg)", "Max angular distance (hypot of yaw/pitch) off for attack").withBounds(0.5, 2.0, 6.0).withAccuracy(1); public final SliderWidget smoothingMultiplier = new SliderWidget("Smoothing Multiplier", "Scales duration based on angle").withBounds(0.5, 1.0, 3.0).withAccuracy(2); public final SliderWidget bezierInfluence = new SliderWidget("Bezier Influence", "0 = linear, 1 = full bezier curve").withBounds(0.0, 1.0, 1.0).withAccuracy(2); @@ -243,7 +243,9 @@ private boolean aimAlignmentOk(ClientPlayerEntity player, LivingEntity target) { float dyaw = MathHelper.wrapDegrees(player.getYaw() - want[0]); float dpitch = want[1] - player.getPitch(); float tol = (float) Math.max(0.1, aimToleranceDeg.getRawState()); - return Math.abs(dyaw) < tol && Math.abs(dpitch) < tol; + // Use combined angular distance (circular region) consistent with AutoBow. + double angDist = Math.hypot(dyaw, dpitch); + return angDist <= tol; } private float[] evalPlanStep() { diff --git a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java index 28ee3de9..276d0ca0 100644 --- a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java +++ b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java @@ -6,46 +6,45 @@ import dev.cigarette.mixin.KeyBindingAccessor; import dev.cigarette.module.TickModule; import dev.cigarette.module.combat.AutoClicker; -import dev.cigarette.module.combat.PerfectHit; import dev.cigarette.module.combat.PlayerAimbot; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.option.KeyBinding; import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.util.collection.DefaultedList; -import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.HitResult; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; +import java.util.Objects; import java.util.Optional; public class AutoBow extends TickModule { public static final dev.cigarette.module.murdermystery.AutoBow INSTANCE = new dev.cigarette.module.murdermystery.AutoBow("murdermystery.autobow", "AutoBow", "Automatically aims and fires a bow at the murderer."); - private final SliderWidget shootDelay = new SliderWidget("Shoot Delay", "Maximum range to target players").withBounds(20, 45, 60).withAccuracy(1); + private final SliderWidget shootDelay = new SliderWidget("Shoot Delay", "Maximum delay to draw before shooting").withBounds(20, 45, 60).withAccuracy(1); private final ToggleWidget genericMode = new ToggleWidget("Generic Mode", "Use PlayerAimbot target without role checks").withDefaultState(false); - + private final SliderWidget targetRange = new SliderWidget("Max Range", "Maximum range to shoot a target.").withBounds(3, 5, 15); private final ToggleWidget prediction = new ToggleWidget("Prediction", "Predict target position for bow").withDefaultState(false); private final SliderWidget predictionTicks = new SliderWidget("Prediction Ticks", "Ticks ahead to predict").withBounds(0, 5, 20).withAccuracy(1); - + // AutoBow intentionally relies on PlayerAimbot's aimToleranceDeg to keep a single synchronized tolerance. private boolean paOldEnableState, paOldPredictionState, paMMOldState = false; private double paOldPredictionTicks; private boolean isMurderer = false; + // Reused as draw ticks counter private int aimTicks = 0; private int itemSlot; + private boolean drawing = false; // whether we are currently holding right-click to draw the bow private AutoBow(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(shootDelay, genericMode, prediction, predictionTicks); + this.setChildren(shootDelay, targetRange, genericMode, prediction, predictionTicks); shootDelay.registerConfigKey(id + ".shootDelay"); + targetRange.registerConfigKey(id + ".targetRange"); genericMode.registerConfigKey(id + ".genericMode"); prediction.registerConfigKey(id + ".prediction"); predictionTicks.registerConfigKey(id + ".predictionTicks"); @@ -55,18 +54,25 @@ private AutoBow(String id, String name, String tooltip) { protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { client.attackCooldown = 0; HitResult hitResult = client.crosshairTarget; - if (hitResult.getType() == HitResult.Type.MISS) {} + if (hitResult == null) return; KeyBinding aimKey = KeyBinding.byId("key.use"); - KeyBindingAccessor aimAccessor = (KeyBindingAccessor) aimKey; - if (hitResult == null || aimKey == null || aimKey.isPressed()) return; + if (aimKey == null) return; LivingEntity activeTarget = PlayerAimbot.INSTANCE.activeTarget; - if (activeTarget == null) return; - + if (activeTarget == null) { + // No target -> reset drawing state + if (drawing) { + aimKey.setPressed(false); + drawing = false; + aimTicks = 0; + } + return; + } + + // Role / item selection logic (unchanged) ------------------------------------------------- if (!genericMode.getRawState()) { Optional tPlayer = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == activeTarget).findFirst(); if (tPlayer.isPresent()) { - MurderMysteryAgent.PersistentPlayer.Role targetRole = tPlayer.get().role; Optional self = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == MinecraftClient.getInstance().player).findFirst(); if (self.isPresent()) { if (self.get().role == MurderMysteryAgent.PersistentPlayer.Role.MURDERER) { @@ -75,8 +81,9 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); for (int ix = 0; ix < is.toArray().length; ix++) { if (is.get(ix).equals(i)) { - assert MinecraftClient.getInstance().player != null; - MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); + if (MinecraftClient.getInstance().player != null) { + MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); + } this.itemSlot = ix; } } @@ -85,8 +92,9 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); for (int ix = 0; ix < is.toArray().length; ix++) { if (MurderMysteryAgent.isDetectiveItem(is.get(ix))) { - assert MinecraftClient.getInstance().player != null; - MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); + if (MinecraftClient.getInstance().player != null) { + MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); + } this.itemSlot = ix; } } @@ -94,46 +102,57 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, } } } + // ----------------------------------------------------------------------------------------- - boolean shouldAim = true; - if (this.aimTicks >= this.shootDelay.getRawState().intValue()) { - shouldAim = false; - } else if (genericMode.getRawState() && activeTarget != null && hitResult.getType() == HitResult.Type.ENTITY) { - EntityHitResult entityHit = (EntityHitResult) hitResult; - if (entityHit.getEntity() == activeTarget) { - shouldAim = false; - } - } - - assert MinecraftClient.getInstance().player != null; - if (!genericMode.getRawState() && MinecraftClient.getInstance().player.getInventory().getSelectedSlot() != this.itemSlot) { + if (!genericMode.getRawState() && MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.getInventory().getSelectedSlot() != this.itemSlot) { + // Selected slot changed away from bow/detective item -> reset aimKey.setPressed(false); + drawing = false; this.aimTicks = 0; return; } - if (shouldAim) { - aimKey.setPressed(true); - aimTicks++; + double aimTolerance = Math.toRadians(PlayerAimbot.INSTANCE.aimToleranceDeg.getRawState()); + double yawDiff = Math.toRadians(Math.abs(activeTarget.getYaw() - player.getYaw())); + double pitchDiff = Math.toRadians(Math.abs(activeTarget.getPitch() - player.getPitch())); + double angularDistance = Math.hypot(yawDiff, pitchDiff); + boolean angleAligned = angularDistance <= aimTolerance; + boolean inRange = player.squaredDistanceTo(activeTarget) <= Math.pow(this.targetRange.getRawState(), 2); + boolean holdingBow = MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.getMainHandStack().getItem().toString().toLowerCase().contains("bow"); + + // Core shooting logic: start drawing only when aligned & in range & holding a bow. + if (angleAligned && inRange && holdingBow) { + if (!drawing) { + // Begin drawing + aimKey.setPressed(true); + drawing = true; + aimTicks = 0; + } else { + aimTicks++; + int requiredTicks = this.shootDelay.getRawState().intValue(); + if (aimTicks >= requiredTicks) { + // Release to fire + aimKey.setPressed(false); + drawing = false; + aimTicks = 0; + } + } } else { - aimKey.setPressed(false); - this.aimTicks = 0; + // Not aligned/in range or not holding bow: ensure we are not drawing + if (drawing) { + aimKey.setPressed(false); + drawing = false; + aimTicks = 0; + } } - - // if (this.aimTicks < this.shootDelay.getRawState().intValue()) { - // assert MinecraftClient.getInstance().player != null; - // if (MinecraftClient.getInstance().player.getInventory().getSelectedSlot() != this.itemSlot) { - // aimKey.setPressed(false); - // this.aimTicks = 0; - // return; - // } - // aimKey.setPressed(true); - // aimTicks++; - // } else { - // aimKey.setPressed(false); - // this.aimTicks = 0; - // } + // Safety: if key binding timesPressed accumulates from other logic reset it when idle + if (!drawing) { + KeyBindingAccessor accessor = (KeyBindingAccessor) Objects.requireNonNull(KeyBinding.byId("key.use")); + if (accessor.getTimesPressed() > 0) { + accessor.setTimesPressed(0); + } + } } @Override @@ -150,6 +169,8 @@ protected void whenEnabled() { if (!genericMode.getRawState()) { PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(true); } + drawing = false; + aimTicks = 0; } @Override @@ -158,5 +179,10 @@ protected void whenDisabled() { PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(this.paMMOldState); PlayerAimbot.INSTANCE.prediction.setRawState(this.paOldPredictionState); PlayerAimbot.INSTANCE.predictionTicks.setRawState(this.paOldPredictionTicks); + // Ensure key released + KeyBinding aimKey = KeyBinding.byId("key.use"); + if (aimKey != null) aimKey.setPressed(false); + drawing = false; + aimTicks = 0; } } From aefc76d23ef13072098a8229f4e1b1157fa6189a Mon Sep 17 00:00:00 2001 From: AH Date: Sat, 6 Sep 2025 12:16:57 -0400 Subject: [PATCH 18/23] fix: occasionally autobow switches to arrow instead of bow --- .../module/murdermystery/AutoBow.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java index 276d0ca0..0e852230 100644 --- a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java +++ b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java @@ -13,6 +13,7 @@ import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.LivingEntity; import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; import net.minecraft.util.collection.DefaultedList; import net.minecraft.util.hit.HitResult; import org.jetbrains.annotations.NotNull; @@ -28,17 +29,16 @@ public class AutoBow extends TickModule { private final SliderWidget targetRange = new SliderWidget("Max Range", "Maximum range to shoot a target.").withBounds(3, 5, 15); private final ToggleWidget prediction = new ToggleWidget("Prediction", "Predict target position for bow").withDefaultState(false); private final SliderWidget predictionTicks = new SliderWidget("Prediction Ticks", "Ticks ahead to predict").withBounds(0, 5, 20).withAccuracy(1); - // AutoBow intentionally relies on PlayerAimbot's aimToleranceDeg to keep a single synchronized tolerance. + private boolean paOldEnableState, paOldPredictionState, paMMOldState = false; private double paOldPredictionTicks; private boolean isMurderer = false; - // Reused as draw ticks counter private int aimTicks = 0; private int itemSlot; - private boolean drawing = false; // whether we are currently holding right-click to draw the bow + private boolean drawing = false; private AutoBow(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); @@ -60,7 +60,6 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, LivingEntity activeTarget = PlayerAimbot.INSTANCE.activeTarget; if (activeTarget == null) { - // No target -> reset drawing state if (drawing) { aimKey.setPressed(false); drawing = false; @@ -69,7 +68,6 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, return; } - // Role / item selection logic (unchanged) ------------------------------------------------- if (!genericMode.getRawState()) { Optional tPlayer = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == activeTarget).findFirst(); if (tPlayer.isPresent()) { @@ -91,7 +89,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, isMurderer = false; DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); for (int ix = 0; ix < is.toArray().length; ix++) { - if (MurderMysteryAgent.isDetectiveItem(is.get(ix))) { + if (is.get(ix).isOf(Items.BOW)) { if (MinecraftClient.getInstance().player != null) { MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); } @@ -102,10 +100,8 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, } } } - // ----------------------------------------------------------------------------------------- if (!genericMode.getRawState() && MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.getInventory().getSelectedSlot() != this.itemSlot) { - // Selected slot changed away from bow/detective item -> reset aimKey.setPressed(false); drawing = false; this.aimTicks = 0; @@ -120,10 +116,8 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, boolean inRange = player.squaredDistanceTo(activeTarget) <= Math.pow(this.targetRange.getRawState(), 2); boolean holdingBow = MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.getMainHandStack().getItem().toString().toLowerCase().contains("bow"); - // Core shooting logic: start drawing only when aligned & in range & holding a bow. if (angleAligned && inRange && holdingBow) { if (!drawing) { - // Begin drawing aimKey.setPressed(true); drawing = true; aimTicks = 0; @@ -131,14 +125,12 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, aimTicks++; int requiredTicks = this.shootDelay.getRawState().intValue(); if (aimTicks >= requiredTicks) { - // Release to fire aimKey.setPressed(false); drawing = false; aimTicks = 0; } } } else { - // Not aligned/in range or not holding bow: ensure we are not drawing if (drawing) { aimKey.setPressed(false); drawing = false; @@ -146,7 +138,6 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, } } - // Safety: if key binding timesPressed accumulates from other logic reset it when idle if (!drawing) { KeyBindingAccessor accessor = (KeyBindingAccessor) Objects.requireNonNull(KeyBinding.byId("key.use")); if (accessor.getTimesPressed() > 0) { @@ -179,7 +170,6 @@ protected void whenDisabled() { PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(this.paMMOldState); PlayerAimbot.INSTANCE.prediction.setRawState(this.paOldPredictionState); PlayerAimbot.INSTANCE.predictionTicks.setRawState(this.paOldPredictionTicks); - // Ensure key released KeyBinding aimKey = KeyBinding.byId("key.use"); if (aimKey != null) aimKey.setPressed(false); drawing = false; From e0f1aefaa51ded6c245f3cb04d00684df2595a73 Mon Sep 17 00:00:00 2001 From: AH Date: Sat, 6 Sep 2025 13:14:57 -0400 Subject: [PATCH 19/23] fix: attempt 32767 to make autobow work --- .../module/murdermystery/AutoBow.java | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java index 0e852230..37f2646a 100644 --- a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java +++ b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java @@ -19,6 +19,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Objects; +import net.minecraft.util.Hand; import java.util.Optional; public class AutoBow extends TickModule { @@ -57,6 +58,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, if (hitResult == null) return; KeyBinding aimKey = KeyBinding.byId("key.use"); if (aimKey == null) return; + KeyBindingAccessor aimKeyAccessor = (KeyBindingAccessor) aimKey; LivingEntity activeTarget = PlayerAimbot.INSTANCE.activeTarget; if (activeTarget == null) { @@ -112,13 +114,16 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, double yawDiff = Math.toRadians(Math.abs(activeTarget.getYaw() - player.getYaw())); double pitchDiff = Math.toRadians(Math.abs(activeTarget.getPitch() - player.getPitch())); double angularDistance = Math.hypot(yawDiff, pitchDiff); - boolean angleAligned = angularDistance <= aimTolerance; + double angularDistanceDeg = Math.toDegrees(angularDistance); boolean inRange = player.squaredDistanceTo(activeTarget) <= Math.pow(this.targetRange.getRawState(), 2); - boolean holdingBow = MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.getMainHandStack().getItem().toString().toLowerCase().contains("bow"); + boolean holdingBow = player.getMainHandStack().isOf(Items.BOW); + double baseTol = PlayerAimbot.INSTANCE.aimToleranceDeg.getRawState(); + double activeTol = drawing ? baseTol * 1.5 : baseTol; + boolean angleAligned = angularDistanceDeg <= activeTol; if (angleAligned && inRange && holdingBow) { if (!drawing) { - aimKey.setPressed(true); + aimKeyAccessor.setTimesPressed(aimKeyAccessor.getTimesPressed() + 1); drawing = true; aimTicks = 0; } else { @@ -126,22 +131,31 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, int requiredTicks = this.shootDelay.getRawState().intValue(); if (aimTicks >= requiredTicks) { aimKey.setPressed(false); - drawing = false; - aimTicks = 0; } } + } else if (inRange && holdingBow) { + aimKey.setPressed(false); + if (angleAligned) { + aimKeyAccessor.setTimesPressed(aimKeyAccessor.getTimesPressed() + 1); + drawing = true; + aimTicks = 0; + } } else { - if (drawing) { + if (angularDistanceDeg > baseTol * 2.0) { aimKey.setPressed(false); drawing = false; aimTicks = 0; - } - } - - if (!drawing) { - KeyBindingAccessor accessor = (KeyBindingAccessor) Objects.requireNonNull(KeyBinding.byId("key.use")); - if (accessor.getTimesPressed() > 0) { - accessor.setTimesPressed(0); + } else { + if (!player.isUsingItem() && client.interactionManager != null) { + client.interactionManager.interactItem(player, Hand.MAIN_HAND); + } + aimTicks++; + int requiredTicks = this.shootDelay.getRawState().intValue(); + if (aimTicks >= requiredTicks) { + aimKey.setPressed(false); + drawing = false; + aimTicks = 0; + } } } } From 5691bacb17d242b42bb427969eebb84d6d6e99ba Mon Sep 17 00:00:00 2001 From: AH Date: Sat, 6 Sep 2025 21:24:06 -0400 Subject: [PATCH 20/23] feat: autobow worksgit add --all! AND doesn't flaggit add --all --- src/main/java/dev/cigarette/lib/AimingL.java | 100 +++-- .../cigarette/module/combat/PlayerAimbot.java | 86 +++- .../module/murdermystery/AutoBow.java | 390 ++++++++++++------ .../dev/cigarette/module/zombies/Aimbot.java | 2 +- 4 files changed, 403 insertions(+), 175 deletions(-) diff --git a/src/main/java/dev/cigarette/lib/AimingL.java b/src/main/java/dev/cigarette/lib/AimingL.java index b1be8614..cbdacfca 100644 --- a/src/main/java/dev/cigarette/lib/AimingL.java +++ b/src/main/java/dev/cigarette/lib/AimingL.java @@ -21,6 +21,8 @@ import net.minecraft.util.math.Vec3d; import org.jetbrains.annotations.NotNull; +import javax.swing.text.html.parser.DTD; + public class AimingL { /** * Compute yaw/pitch angles (in degrees) to look from 'from' to 'to'. @@ -30,10 +32,7 @@ public static float[] anglesFromTo(Vec3d from, Vec3d to) { Vec3d d = to.subtract(from); double lenSq = d.lengthSquared(); if (!isFinite(d.x) || !isFinite(d.y) || !isFinite(d.z) || lenSq < 1.0e-12) { - ClientPlayerEntity p = MinecraftClient.getInstance().player; - float fallbackYaw = p != null ? MathHelper.wrapDegrees(p.getYaw()) : 0f; - float fallbackPitch = p != null ? MathHelper.clamp(p.getPitch(), -90f, 90f) : 0f; - return new float[]{fallbackYaw, fallbackPitch}; + return fallbackPE(); } Vec3d v = d.normalize(); float yaw = (float) Math.toDegrees(Math.atan2(-v.x, v.z)); @@ -43,14 +42,18 @@ public static float[] anglesFromTo(Vec3d from, Vec3d to) { yaw = limitAccuracy(yaw, 100); pitch = limitAccuracy(pitch, 100); if (!Float.isFinite(yaw) || !Float.isFinite(pitch)) { - ClientPlayerEntity p = MinecraftClient.getInstance().player; - float fallbackYaw = p != null ? MathHelper.wrapDegrees(p.getYaw()) : 0f; - float fallbackPitch = p != null ? MathHelper.clamp(p.getPitch(), -90f, 90f) : 0f; - return new float[]{fallbackYaw, fallbackPitch}; + return fallbackPE(); } return new float[]{yaw, pitch}; } + private static float[] fallbackPE() { + ClientPlayerEntity p = MinecraftClient.getInstance().player; + float fallbackYaw = p != null ? MathHelper.wrapDegrees(p.getYaw()) : 0f; + float fallbackPitch = p != null ? MathHelper.clamp(p.getPitch(), -90f, 90f) : 0f; + return new float[]{fallbackYaw, fallbackPitch}; + } + /** * Generate a random signed double in the range [-max, -min] U [min, max]. * If max <= 0, returns 0.0. @@ -143,12 +146,10 @@ public static void doSprint(boolean sprint) { if (sprint) { if (!player.isSprinting() && !player.isUsingItem() && !player.isSneaking()) { player.setSprinting(true); - player.networkHandler.sendPacket(new ClientCommandC2SPacket(player, ClientCommandC2SPacket.Mode.START_SPRINTING)); } } else { if (player.isSprinting()) { player.setSprinting(false); - player.networkHandler.sendPacket(new ClientCommandC2SPacket(player, ClientCommandC2SPacket.Mode.STOP_SPRINTING)); } } } @@ -179,53 +180,74 @@ public static void switchToBestPvPWeapon(ClientPlayerEntity player, double dista * Send a PlayerInteractItemC2SPacket with the given yaw/pitch and a new sequence number. * Uses PendingUpdateManager to ensure proper sequencing. */ - public static void sendAimPacket(ClientWorld world, ClientPlayerEntity player, float yaw, float pitch) { - if (world == null || player == null) return; - // Keep yaw continuous relative to current player yaw to avoid modulo-360 jumps + public static void sendAimPacket(ClientPlayerEntity player, float yaw, float pitch) { + if (player == null) return; float cur = player.getYaw(); float syaw = Float.isFinite(yaw) ? (cur + MathHelper.wrapDegrees(yaw - cur)) : cur; float spitch = sanitizePitch(pitch); - ClientWorldAccessor accessor = (ClientWorldAccessor) world; - try (PendingUpdateManager pum = accessor.getPendingUpdateManager().incrementSequence()) { - int seq = pum.getSequence(); - player.networkHandler.sendPacket(new PlayerInteractItemC2SPacket(Hand.MAIN_HAND, seq, syaw, spitch)); - } + player.setYaw(syaw); + player.setPitch(spitch); } - /** - * Send a look packet followed by an attack and a hand swing. Useful for revive/auras that need to set look client-side. - */ public static void lookAndAttack(ClientWorld world, ClientPlayerEntity player, LivingEntity target, float yaw, float pitch) { if (player == null || target == null || world == null) return; HitResult hitResult = MinecraftClient.getInstance().crosshairTarget; KeyBinding attackKey = KeyBinding.byId("key.attack"); + if (attackKey == null) return; KeyBindingAccessor attackKeyAccessor = (KeyBindingAccessor) attackKey; - if (hitResult == null || attackKey == null || !attackKey.isPressed()) return; - if (hitResult.getType() == HitResult.Type.ENTITY) { + + boolean shouldAttack = false; + + if (hitResult != null && hitResult.getType() == HitResult.Type.ENTITY) { EntityHitResult entityHitResult = (EntityHitResult) hitResult; Entity entity = entityHitResult.getEntity(); - if (!(entity instanceof LivingEntity livingEntity)) return; - if (livingEntity.hurtTime > 1) return; - attackKeyAccessor.setTimesPressed(attackKeyAccessor.getTimesPressed() + 1); + if (entity instanceof LivingEntity livingEntity && livingEntity == target) { + if (livingEntity.hurtTime <= 1) shouldAttack = true; + } } - } - // Helpers - private static boolean isFinite(double d) { - return !Double.isNaN(d) && !Double.isInfinite(d); + if (!shouldAttack) { + Vec3d eye = player.getEyePos(); + Vec3d toTarget = target.getBoundingBox().getCenter().subtract(eye); + double dist = toTarget.length(); + if (dist > 1e-6) { + double yawRad = Math.toRadians(yaw); + double pitchRad = Math.toRadians(pitch); + double dx = -Math.sin(yawRad) * Math.cos(pitchRad); + double dy = -Math.sin(pitchRad); + double dz = Math.cos(yawRad) * Math.cos(pitchRad); + Vec3d dir = new Vec3d(dx, dy, dz).normalize(); + Vec3d toNorm = toTarget.normalize(); + double dot = Math.max(-1.0, Math.min(1.0, dir.dotProduct(toNorm))); + double angleDeg = Math.toDegrees(Math.acos(dot)); + if (angleDeg <= 8.0 && dist <= 4.0 && target.hurtTime <= 1) { + shouldAttack = true; + } + } + } + + if (!shouldAttack) return; + + sendAimPacket(player, yaw, pitch); + + attackKeyAccessor.setTimesPressed(attackKeyAccessor.getTimesPressed() + 1); + MinecraftClient.getInstance().attackCooldown = 0; } + private static float sanitizeYaw(float yaw) { - if (!Float.isFinite(yaw)) yaw = 0f; return MathHelper.wrapDegrees(yaw); } - // Keep for API parity even if unused internally - private static float limitAccuracy(float yaw, int precision) { - if (precision <= 0) return yaw; - float factor = 1f / precision; - return Math.round(yaw * precision) * factor; - } + private static float sanitizePitch(float pitch) { - if (!Float.isFinite(pitch)) pitch = 0f; return MathHelper.clamp(pitch, -90f, 90f); } -} \ No newline at end of file + + private static float limitAccuracy(float v, int accuracy) { + if (accuracy <= 0) return v; + return (float) (Math.round(v * accuracy) / (double) accuracy); + } + + private static boolean isFinite(double v) { + return Double.isFinite(v); + } +} diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index f0b4b355..77059636 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -61,10 +61,12 @@ public class PlayerAimbot extends TickModule { public final ToggleWidget prediction = new ToggleWidget("Prediction", "Predict target position").withDefaultState(false); public final SliderWidget predictionTicks = new SliderWidget("Prediction Ticks", "Ticks ahead to predict").withBounds(0, 5, 20).withAccuracy(1); + // New: minimum angular gap (deg) before initiating an aim plan & basis for dynamic prediction scaling + public final SliderWidget engageAimAngle = new SliderWidget("Engage Angle (deg)", "Only begin aim plan when target angle gap >= this; also scales prediction").withBounds(1.0, 6.0, 30.0).withAccuracy(1); private PlayerAimbot(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(autoAttack, smoothAim, aimRange, prediction, predictionTicks, wTap, attackCps, ignoreTeammates, lockOnKeybind, testMode, + this.setChildren(autoAttack, smoothAim, aimRange, prediction, predictionTicks, engageAimAngle, wTap, attackCps, ignoreTeammates, lockOnKeybind, testMode, murderMysteryMode, detectiveAim, jitterViolence, driftViolence, aimToleranceDeg, smoothingMultiplier, bezierInfluence, controlJitterScale, interpolationMode, interferenceAngleDeg, interferenceGraceTicks); @@ -73,6 +75,7 @@ private PlayerAimbot(String id, String name, String tooltip) { aimRange.registerConfigKey(id + ".aimRange"); prediction.registerConfigKey(id + ".prediction"); predictionTicks.registerConfigKey(id + ".predictionTicks"); + engageAimAngle.registerConfigKey(id + ".engageAimAngle"); wTap.registerConfigKey(id + ".wTap"); attackCps.registerConfigKey(id + ".attackCps"); ignoreTeammates.registerConfigKey(id + ".ignoreTeammates"); @@ -171,15 +174,39 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, return; } - Vec3d aimPoint = computeAimPoint(player, target, false); + // Compute raw (non-predicted) angle gap first + double angleGap = computeAngleGap(player, target); + double engageThresh = Math.max(0.5, engageAimAngle.getRawState()); + + // Dynamically boost prediction ticks when large angle corrections are needed + int basePred = (int) Math.round(predictionTicks.getRawState()); + int dynamicPred = basePred; + if (prediction.getRawState() && angleGap >= engageThresh) { + // scale extra ticks up to +10 based on how many thresholds we exceed (linear) + double factor = (angleGap - engageThresh) / engageThresh; // 0 at threshold + int extra = (int) Math.min(10, Math.max(0, Math.round(factor * 4))); // each threshold multiple ~+4 ticks + dynamicPred = Math.min(30, basePred + extra); // hard cap + } + + Vec3d aimPoint = computeAimPoint(player, target, false, dynamicPred); float[] targetAngles = AimingL.anglesFromTo(player.getEyePos(), aimPoint); float curYaw = getContinuousYaw(player); float curPitch = MathHelper.clamp(player.getPitch(), -90f, 90f); - if (!isPlanActive() || !Objects.equals(target.getUuid(), activeTargetId) || planTargetChangedSignificantly(targetAngles)) { - buildBezierPlan(curYaw, curPitch, targetAngles[0], targetAngles[1]); - activeTarget = target; - activeTargetId = target.getUuid(); + boolean needPlan = !isPlanActive() || !Objects.equals(target.getUuid(), activeTargetId) || planTargetChangedSignificantly(targetAngles); + if (needPlan) { + // Only start a NEW plan if we exceed engage threshold OR no active plan yet (to allow first snap if already close) + if (angleGap >= engageThresh || !isPlanActive()) { + buildBezierPlan(curYaw, curPitch, targetAngles[0], targetAngles[1]); + activeTarget = target; + activeTargetId = target.getUuid(); + } + } + + if (!isPlanActive() && angleGap < engageThresh) { + // Skip micro-adjust aiming until gap large enough + lastAppliedValid = false; // allow re-unwrap when we re-engage + return; } float[] stepAngles = evalPlanStep(); @@ -195,7 +222,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, boolean aimAligned = aimAlignmentOk(player, target); if (withinRange && aimAligned && ticksUntilNextAttack <= 0) { if (wTap.getRawState()) AimingL.doSprint(true); - Vec3d atkAim = computeAimPoint(player, target, true); + Vec3d atkAim = computeAimPoint(player, target, true, dynamicPred); float[] atkAngles = AimingL.anglesFromTo(player.getEyePos(), atkAim); AimingL.lookAndAttack(world, player, target, atkAngles[0], atkAngles[1]); scheduleNextAttack(); @@ -239,13 +266,15 @@ private void scheduleNextAttack() { private boolean aimAlignmentOk(ClientPlayerEntity player, LivingEntity target) { Vec3d aimPoint = computeAimPoint(player, target, false); - float[] want = AimingL.anglesFromTo(player.getEyePos(), aimPoint); - float dyaw = MathHelper.wrapDegrees(player.getYaw() - want[0]); - float dpitch = want[1] - player.getPitch(); - float tol = (float) Math.max(0.1, aimToleranceDeg.getRawState()); - // Use combined angular distance (circular region) consistent with AutoBow. - double angDist = Math.hypot(dyaw, dpitch); - return angDist <= tol; + Vec3d lookVec = player.getRotationVec(1.0F); + Vec3d toTarget = aimPoint.subtract(player.getEyePos()); + if (toTarget.lengthSquared() == 0) return true; + lookVec = lookVec.normalize(); + Vec3d toNorm = toTarget.normalize(); + double dot = Math.max(-1.0, Math.min(1.0, lookVec.dotProduct(toNorm))); + double angleDeg = Math.toDegrees(Math.acos(dot)); + double tol = Math.max(0.1, aimToleranceDeg.getRawState()); + return angleDeg <= tol; } private float[] evalPlanStep() { @@ -320,12 +349,25 @@ private float applyInterpolation(float t) { } } - private boolean isPlanActive() { return planTicksElapsed < planTicksTotal; } + private boolean isPlanActive() { + return planTicksElapsed < planTicksTotal; + } + + private double computeAngleGap(ClientPlayerEntity player, LivingEntity target) { + Vec3d eye = player.getEyePos(); + Vec3d to = target.getBoundingBox().getCenter().subtract(eye); + if (to.lengthSquared() == 0) return 0.0; + Vec3d look = player.getRotationVec(1.0F).normalize(); + Vec3d dir = to.normalize(); + double dot = Math.max(-1, Math.min(1, look.dotProduct(dir))); + return Math.toDegrees(Math.acos(dot)); + } private boolean planTargetChangedSignificantly(float[] targetAngles) { float dyaw = Math.abs(MathHelper.wrapDegrees(endYaw - targetAngles[0])); float dpitch = Math.abs(endPitch - targetAngles[1]); - return dyaw > 3.0f || dpitch > 3.0f; + double thresh = Math.max(1.0, engageAimAngle.getRawState()); + return dyaw > thresh || dpitch > thresh; } private void clearPlan() { @@ -337,7 +379,7 @@ private void clearPlan() { private static float cubicBezier(float p0, float p1, float p2, float p3, float t) { float u = 1f - t; - return u*u*u*p0 + 3*u*u*t*p1 + 3*u*t*t*p2 + t*t*t*p3; + return u * u * u * p0 + 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3; } private static float easeInOutCubic(float t) { @@ -356,16 +398,22 @@ private float getContinuousYaw(ClientPlayerEntity player) { return lastAppliedValid ? unwrapTowards(wrappedCurrent, lastAppliedYaw) : wrappedCurrent; } - private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, boolean attackNow) { + private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, boolean attackNow, int dynamicTicks) { if (prediction.getRawState()) { Vec3d vel = target.getVelocity(); - Vec3d predictedPos = target.getPos().add(vel.multiply(predictionTicks.getRawState())); + int ticks = Math.max(0, dynamicTicks); + Vec3d predictedPos = target.getPos().add(vel.multiply(ticks)); return predictedPos.add(0, target.getHeight() * 0.5, 0); } else { return AimingL.getAimPointInsideHitbox(player, target, attackNow, 0.1, 0.6, 2.5); } } + // Retain original signature for any other callers; delegate to dynamic with base ticks + private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, boolean attackNow) { + return computeAimPoint(player, target, attackNow, (int) Math.round(predictionTicks.getRawState())); + } + private boolean isMurderMysteryActive() { return murderMysteryMode.getRawState(); } diff --git a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java index 37f2646a..8272f46d 100644 --- a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java +++ b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java @@ -15,178 +15,336 @@ import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.Vec3d; import org.jetbrains.annotations.NotNull; -import java.util.Objects; -import net.minecraft.util.Hand; import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; +/** + * AutoBow – draws, holds, aims and fires the bow once alignment is acceptable. + * Fixes implemented: + * - Removed premature slot switching while still aiming within viable tolerance. + * - Added hold grace (lostHoldTicks) with decay to avoid jitter-based cancellations. + * - Added configurable holdGraceTicks slider. + * - Respect switchSlotOnFail toggle before switching slots. + * - Proper tolerance scaling after long inactivity ( >60 / >120 ticks since last fire ). + * - Simplified and de-duplicated initialization logic. + */ public class AutoBow extends TickModule { - public static final dev.cigarette.module.murdermystery.AutoBow INSTANCE = new dev.cigarette.module.murdermystery.AutoBow("murdermystery.autobow", "AutoBow", "Automatically aims and fires a bow at the murderer."); + public static final AutoBow INSTANCE = new AutoBow("murdermystery.autobow", "AutoBow", "Automatically aims and fires a bow at the murderer."); - private final SliderWidget shootDelay = new SliderWidget("Shoot Delay", "Maximum delay to draw before shooting").withBounds(20, 45, 60).withAccuracy(1); - private final ToggleWidget genericMode = new ToggleWidget("Generic Mode", "Use PlayerAimbot target without role checks").withDefaultState(false); + private final SliderWidget shootDelay = new SliderWidget("Shoot Delay", "Maximum delay (ticks) to fully draw before shooting").withBounds(20, 45, 60).withAccuracy(1); private final SliderWidget targetRange = new SliderWidget("Max Range", "Maximum range to shoot a target.").withBounds(3, 5, 15); - private final ToggleWidget prediction = new ToggleWidget("Prediction", "Predict target position for bow").withDefaultState(false); + private final ToggleWidget genericMode = new ToggleWidget("Generic Mode", "Use PlayerAimbot target without Murder Mystery prioritization").withDefaultState(false); + private final ToggleWidget prediction = new ToggleWidget("Prediction", "Use PlayerAimbot prediction settings while active").withDefaultState(false); private final SliderWidget predictionTicks = new SliderWidget("Prediction Ticks", "Ticks ahead to predict").withBounds(0, 5, 20).withAccuracy(1); + private final SliderWidget holdAngleTolerance = new SliderWidget("Hold Angle", "Angle (deg) allowed while continuing to hold draw").withBounds(5, 25, 60).withAccuracy(1); + private final SliderWidget holdGraceTicks = new SliderWidget("Hold Grace", "Ticks outside hold tolerance before cancelling").withBounds(1, 3, 12).withAccuracy(0); + private final ToggleWidget switchSlotOnFail = new ToggleWidget("Slot Switch Fail", "Switch slot after failed shot alignment").withDefaultState(true); - private boolean paOldEnableState, paOldPredictionState, paMMOldState = false; - + private boolean paOldEnableState; + private boolean paOldPredictionState; + private boolean paOldMMState; private double paOldPredictionTicks; + private Boolean paMMOldState = false; - private boolean isMurderer = false; + private boolean isMurderer = false; // kept for potential HUD usage - private int aimTicks = 0; - private int itemSlot; + private int drawTicks = 0; + private int requiredDrawTicks = 20; private boolean drawing = false; + private int ticksSinceLastFire = 0; + private int lostHoldTicks = 0; private AutoBow(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(shootDelay, targetRange, genericMode, prediction, predictionTicks); + this.setChildren(shootDelay, targetRange, genericMode, prediction, predictionTicks, holdAngleTolerance, holdGraceTicks, switchSlotOnFail); shootDelay.registerConfigKey(id + ".shootDelay"); targetRange.registerConfigKey(id + ".targetRange"); genericMode.registerConfigKey(id + ".genericMode"); - prediction.registerConfigKey(id + ".prediction"); predictionTicks.registerConfigKey(id + ".predictionTicks"); + holdAngleTolerance.registerConfigKey(id + ".holdAngleTolerance"); + holdGraceTicks.registerConfigKey(id + ".holdGraceTicks"); + switchSlotOnFail.registerConfigKey(id + ".switchSlotOnFail"); } @Override - protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { - client.attackCooldown = 0; - HitResult hitResult = client.crosshairTarget; - if (hitResult == null) return; - KeyBinding aimKey = KeyBinding.byId("key.use"); - if (aimKey == null) return; - KeyBindingAccessor aimKeyAccessor = (KeyBindingAccessor) aimKey; - - LivingEntity activeTarget = PlayerAimbot.INSTANCE.activeTarget; - if (activeTarget == null) { - if (drawing) { - aimKey.setPressed(false); - drawing = false; - aimTicks = 0; + public void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { + if (client.currentScreen != null) return; + + ticksSinceLastFire++; + + boolean shouldMM = !genericMode.getRawState(); + if (PlayerAimbot.INSTANCE.murderMysteryMode.getRawState() != shouldMM) { + PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(shouldMM); + releaseIfDrawing(player); + } + + ensureActiveTarget(player); + LivingEntity target = PlayerAimbot.INSTANCE.activeTarget; + if (target != null) { + double maxRangeSq = square(targetRange.getRawState()); + if (!target.isAlive() || target.isRemoved() || player.squaredDistanceTo(target) > maxRangeSq) { + PlayerAimbot.INSTANCE.activeTarget = null; + target = null; } + } + if (target == null) { + releaseIfDrawing(player); return; } if (!genericMode.getRawState()) { - Optional tPlayer = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == activeTarget).findFirst(); - if (tPlayer.isPresent()) { - Optional self = MurderMysteryAgent.getVisiblePlayers().stream().filter((p) -> p.playerEntity == MinecraftClient.getInstance().player).findFirst(); - if (self.isPresent()) { - if (self.get().role == MurderMysteryAgent.PersistentPlayer.Role.MURDERER) { - isMurderer = true; - ItemStack i = self.get().itemStack; - DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); - for (int ix = 0; ix < is.toArray().length; ix++) { - if (is.get(ix).equals(i)) { - if (MinecraftClient.getInstance().player != null) { - MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); - } - this.itemSlot = ix; - } - } - } else { - isMurderer = false; - DefaultedList is = self.get().playerEntity.getInventory().getMainStacks(); - for (int ix = 0; ix < is.toArray().length; ix++) { - if (is.get(ix).isOf(Items.BOW)) { - if (MinecraftClient.getInstance().player != null) { - MinecraftClient.getInstance().player.getInventory().setSelectedSlot(ix); - } - this.itemSlot = ix; - } - } - } - } - } + LivingEntity fTarget = target; + Optional tPlayer = MurderMysteryAgent.getVisiblePlayers().stream().filter(p -> p.playerEntity == fTarget).findFirst(); + isMurderer = tPlayer.filter(pp -> pp.role == MurderMysteryAgent.PersistentPlayer.Role.MURDERER).isPresent(); } - if (!genericMode.getRawState() && MinecraftClient.getInstance().player != null && MinecraftClient.getInstance().player.getInventory().getSelectedSlot() != this.itemSlot) { - aimKey.setPressed(false); - drawing = false; - this.aimTicks = 0; + selectBowIfNeeded(player); + if (!holdingBow(player)) { + releaseIfDrawing(player); return; } - double aimTolerance = Math.toRadians(PlayerAimbot.INSTANCE.aimToleranceDeg.getRawState()); - double yawDiff = Math.toRadians(Math.abs(activeTarget.getYaw() - player.getYaw())); - double pitchDiff = Math.toRadians(Math.abs(activeTarget.getPitch() - player.getPitch())); - double angularDistance = Math.hypot(yawDiff, pitchDiff); - double angularDistanceDeg = Math.toDegrees(angularDistance); - boolean inRange = player.squaredDistanceTo(activeTarget) <= Math.pow(this.targetRange.getRawState(), 2); - boolean holdingBow = player.getMainHandStack().isOf(Items.BOW); + HitResult hr = client.crosshairTarget; + if (hr != null && hr.getType() == HitResult.Type.BLOCK) { // avoid wasting arrows into walls + releaseIfDrawing(player); + return; + } + + if (!drawing) { + if (!canStartDrawing(player, target)) return; + startDrawing(player); + return; + } + + // Already drawing – maintain or fire double baseTol = PlayerAimbot.INSTANCE.aimToleranceDeg.getRawState(); - double activeTol = drawing ? baseTol * 1.5 : baseTol; - boolean angleAligned = angularDistanceDeg <= activeTol; - - if (angleAligned && inRange && holdingBow) { - if (!drawing) { - aimKeyAccessor.setTimesPressed(aimKeyAccessor.getTimesPressed() + 1); - drawing = true; - aimTicks = 0; - } else { - aimTicks++; - int requiredTicks = this.shootDelay.getRawState().intValue(); - if (aimTicks >= requiredTicks) { - aimKey.setPressed(false); - } + double holdTol = holdAngleTolerance.getRawState(); + // dynamic scaling: longer since last fire -> relax a bit + if (ticksSinceLastFire > 120) { + baseTol *= 1.6; + holdTol *= 1.25; + } else if (ticksSinceLastFire > 60) { + baseTol *= 1.4; + holdTol *= 1.15; + } + + double angleActual = computeAngle(player, target); + boolean predEnabled = prediction.getRawState(); + double predictedAngle = Double.NaN; + if (predEnabled) { + int pticks = Math.max(0, predictionTicks.getRawState().intValue()); + Vec3d predicted = target.getPos().add(target.getVelocity().multiply(pticks)) + .add(0, target.getStandingEyeHeight() * 0.5, 0); + predictedAngle = computeAngleToPoint(player, predicted); + } + + boolean withinHold = (!Double.isNaN(angleActual) && angleActual <= holdTol) || (predEnabled && !Double.isNaN(predictedAngle) && predictedAngle <= holdTol); + int graceLimit = Math.max(1, holdGraceTicks.getRawState().intValue()); + if (!withinHold) { + lostHoldTicks++; + if (lostHoldTicks >= graceLimit) { + failRelease(player); + return; } - } else if (inRange && holdingBow) { - aimKey.setPressed(false); - if (angleAligned) { - aimKeyAccessor.setTimesPressed(aimKeyAccessor.getTimesPressed() + 1); - drawing = true; - aimTicks = 0; + } else { + // decay for stability – prevents immediate cancellation after brief jitter + if (lostHoldTicks > 0) lostHoldTicks = Math.max(0, lostHoldTicks - 2); + } + + drawTicks++; + int minHoldTicks = 5; // allow minimal wind-up before considering release + if (drawTicks >= requiredDrawTicks && drawTicks >= minHoldTicks) { + boolean crosshairOn = client.crosshairTarget instanceof EntityHitResult ehr && ehr.getEntity() == target; + boolean actualOk = !Double.isNaN(angleActual) && angleActual <= baseTol; + boolean predictedOk = predEnabled && !Double.isNaN(predictedAngle) && predictedAngle <= baseTol; + if (crosshairOn || actualOk || predictedOk) { + fire(player); } + } + } + + private boolean canStartDrawing(ClientPlayerEntity player, LivingEntity target) { + if (target == null) return false; + double maxRangeSq = square(targetRange.getRawState()); + if (player.squaredDistanceTo(target) > maxRangeSq) return false; + double baseTol = PlayerAimbot.INSTANCE.aimToleranceDeg.getRawState(); + double holdTol = holdAngleTolerance.getRawState(); + double angleActual = computeAngle(player, target); + if (!Double.isNaN(angleActual) && angleActual <= baseTol) return true; + boolean predEnabled = prediction.getRawState(); + if (predEnabled) { + int pticks = Math.max(0, predictionTicks.getRawState().intValue()); + Vec3d predicted = target.getPos().add(target.getVelocity().multiply(pticks)) + .add(0, target.getStandingEyeHeight() * 0.5, 0); + double pAng = computeAngleToPoint(player, predicted); + if (!Double.isNaN(pAng) && pAng <= baseTol) return true; + // allow relaxed start if close to hold tolerance (helps moving targets) + return !Double.isNaN(angleActual) && angleActual <= holdTol; + } + return !Double.isNaN(angleActual) && angleActual <= holdTol; + } + + private void ensureActiveTarget(ClientPlayerEntity player) { + LivingEntity current = PlayerAimbot.INSTANCE.activeTarget; + if (current == null || !current.isAlive() || current.isRemoved()) { + PlayerAimbot.INSTANCE.activeTarget = null; + if (drawing) failRelease(player); + try { + PlayerAimbot.INSTANCE.activeTarget = PlayerAimbot.getBestTargetFor(player); + } catch (Exception ignored) { + PlayerAimbot.INSTANCE.activeTarget = null; + } + } + } + + private void failRelease(ClientPlayerEntity player) { + if (switchSlotOnFail.getRawState()) { + releaseAndMaybeSwitch(player); } else { - if (angularDistanceDeg > baseTol * 2.0) { - aimKey.setPressed(false); - drawing = false; - aimTicks = 0; - } else { - if (!player.isUsingItem() && client.interactionManager != null) { - client.interactionManager.interactItem(player, Hand.MAIN_HAND); - } - aimTicks++; - int requiredTicks = this.shootDelay.getRawState().intValue(); - if (aimTicks >= requiredTicks) { - aimKey.setPressed(false); - drawing = false; - aimTicks = 0; - } + releaseIfDrawing(player); + } + } + + private void releaseAndMaybeSwitch(ClientPlayerEntity player) { + releaseIfDrawing(player); + switchHotbarSlot(player); + } + + private void selectBowIfNeeded(ClientPlayerEntity player) { + if (holdingBow(player)) return; + DefaultedList inv = player.getInventory().getMainStacks(); + for (int i = 0; i < inv.size(); i++) { + if (inv.get(i).isOf(Items.BOW)) { + player.getInventory().setSelectedSlot(i); + break; + } + } + } + + private boolean holdingBow(ClientPlayerEntity player) { + return player.getMainHandStack().isOf(Items.BOW) || player.getOffHandStack().isOf(Items.BOW); + } + + private double computeAngle(ClientPlayerEntity player, LivingEntity target) { + if (target == null) return Double.NaN; + Vec3d eye = player.getEyePos(); + Vec3d to = target.getPos().add(0, target.getStandingEyeHeight() * 0.5, 0).subtract(eye); + if (to.lengthSquared() == 0) return Double.NaN; + Vec3d look = player.getRotationVec(1.0F).normalize(); + Vec3d dir = to.normalize(); + double dot = Math.max(-1, Math.min(1, look.dotProduct(dir))); + return Math.toDegrees(Math.acos(dot)); + } + + private double computeAngleToPoint(ClientPlayerEntity player, Vec3d point) { + Vec3d eye = player.getEyePos(); + Vec3d to = point.subtract(eye); + if (to.lengthSquared() == 0) return 0.0; + Vec3d look = player.getRotationVec(1.0F).normalize(); + Vec3d dir = to.normalize(); + double dot = Math.max(-1, Math.min(1, look.dotProduct(dir))); + return Math.toDegrees(Math.acos(dot)); + } + + private void startDrawing(ClientPlayerEntity player) { + if (!holdingBow(player)) return; + if (player.isUsingItem()) return; + drawing = true; + drawTicks = 0; + lostHoldTicks = 0; + int max = shootDelay.getRawState().intValue(); + if (max < 20) max = 20; + requiredDrawTicks = 20 + (max > 20 ? ThreadLocalRandom.current().nextInt(Math.max(1, max - 19)) : 0); + if (MinecraftClient.getInstance().interactionManager != null) { + KeyBinding binding = KeyBinding.byId("key.use"); + if (binding != null) { + binding.setPressed(true); + KeyBindingAccessor accessor = (KeyBindingAccessor) binding; + accessor.setTimesPressed(accessor.getTimesPressed() + 1); + } + } + } + + private void fire(ClientPlayerEntity player) { + if (!drawing) return; + if (player.isUsingItem()) { + KeyBinding binding = KeyBinding.byId("key.use"); + if (binding != null) { + binding.setPressed(false); + KeyBindingAccessor accessor = (KeyBindingAccessor) binding; + accessor.setTimesPressed(Math.max(0, accessor.getTimesPressed() - 1)); + } + } + drawing = false; + drawTicks = 0; + lostHoldTicks = 0; + ticksSinceLastFire = 0; + } + + private void releaseIfDrawing(ClientPlayerEntity player) { + if (!drawing) return; + fire(player); + } + + private void switchHotbarSlot(ClientPlayerEntity player) { + if (player == null) return; + int current = player.getInventory().getSelectedSlot(); + for (int i = 1; i < 9; i++) { + int next = (current + i) % 9; + ItemStack stack = player.getInventory().getStack(next); + if (!stack.isEmpty() && !stack.isOf(Items.BOW)) { + player.getInventory().setSelectedSlot(next); + return; } } + player.getInventory().setSelectedSlot((current + 1) % 9); } + private double square(double v) { return v * v; } + @Override protected void whenEnabled() { this.paOldEnableState = PlayerAimbot.INSTANCE.getRawState(); this.paMMOldState = PlayerAimbot.INSTANCE.murderMysteryMode.getRawState(); - this.paOldPredictionState = PlayerAimbot.INSTANCE.prediction.getRawState(); - this.paOldPredictionTicks = PlayerAimbot.INSTANCE.predictionTicks.getRawState(); + paOldPredictionState = PlayerAimbot.INSTANCE.prediction.getRawState(); + paOldPredictionTicks = PlayerAimbot.INSTANCE.predictionTicks.getRawState(); + paOldMMState = PlayerAimbot.INSTANCE.murderMysteryMode.getRawState(); - AutoClicker.INSTANCE.widget.setRawState(false); PlayerAimbot.INSTANCE.widget.setRawState(true); - PlayerAimbot.INSTANCE.prediction.setRawState(prediction.getRawState()); + AutoClicker.INSTANCE.widget.setRawState(false); PlayerAimbot.INSTANCE.predictionTicks.setRawState(predictionTicks.getRawState()); - if (!genericMode.getRawState()) { - PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(true); - } + PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(!genericMode.getRawState()); + PlayerAimbot.INSTANCE.prediction.setRawState(prediction.getRawState()); + + isMurderer = false; + drawTicks = 0; + requiredDrawTicks = 20; drawing = false; - aimTicks = 0; + lostHoldTicks = 0; + ticksSinceLastFire = 0; } @Override protected void whenDisabled() { PlayerAimbot.INSTANCE.widget.setRawState(this.paOldEnableState); PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(this.paMMOldState); - PlayerAimbot.INSTANCE.prediction.setRawState(this.paOldPredictionState); - PlayerAimbot.INSTANCE.predictionTicks.setRawState(this.paOldPredictionTicks); - KeyBinding aimKey = KeyBinding.byId("key.use"); - if (aimKey != null) aimKey.setPressed(false); + PlayerAimbot.INSTANCE.prediction.setRawState(paOldPredictionState); + PlayerAimbot.INSTANCE.predictionTicks.setRawState(paOldPredictionTicks); + PlayerAimbot.INSTANCE.murderMysteryMode.setRawState(paOldMMState); + AutoClicker.INSTANCE.widget.setRawState(false); + + MinecraftClient mc = MinecraftClient.getInstance(); + if (mc.interactionManager != null && mc.player != null) { + mc.interactionManager.stopUsingItem(mc.player); + } drawing = false; - aimTicks = 0; + drawTicks = 0; + requiredDrawTicks = 20; + lostHoldTicks = 0; } } diff --git a/src/main/java/dev/cigarette/module/zombies/Aimbot.java b/src/main/java/dev/cigarette/module/zombies/Aimbot.java index e050ae67..68214c71 100644 --- a/src/main/java/dev/cigarette/module/zombies/Aimbot.java +++ b/src/main/java/dev/cigarette/module/zombies/Aimbot.java @@ -80,7 +80,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, PlayerEntityL.setRotationVector(player, vector); } - AimingL.sendAimPacket(world, player, aimYaw, aimPitch); + AimingL.sendAimPacket(player, aimYaw, aimPitch); } } From 70f64a9e709dd4340328f0f3e1b0aa0967b4e71b Mon Sep 17 00:00:00 2001 From: AH Date: Sat, 6 Sep 2025 21:24:38 -0400 Subject: [PATCH 21/23] fmt: yeah ok --- .../dev/cigarette/module/murdermystery/AutoBow.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java index 8272f46d..7813b5fe 100644 --- a/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java +++ b/src/main/java/dev/cigarette/module/murdermystery/AutoBow.java @@ -51,7 +51,7 @@ public class AutoBow extends TickModule { private double paOldPredictionTicks; private Boolean paMMOldState = false; - private boolean isMurderer = false; // kept for potential HUD usage + private boolean isMurderer = false; private int drawTicks = 0; private int requiredDrawTicks = 20; @@ -110,7 +110,7 @@ public void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @N } HitResult hr = client.crosshairTarget; - if (hr != null && hr.getType() == HitResult.Type.BLOCK) { // avoid wasting arrows into walls + if (hr != null && hr.getType() == HitResult.Type.BLOCK) { releaseIfDrawing(player); return; } @@ -121,10 +121,8 @@ public void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @N return; } - // Already drawing – maintain or fire double baseTol = PlayerAimbot.INSTANCE.aimToleranceDeg.getRawState(); double holdTol = holdAngleTolerance.getRawState(); - // dynamic scaling: longer since last fire -> relax a bit if (ticksSinceLastFire > 120) { baseTol *= 1.6; holdTol *= 1.25; @@ -152,12 +150,11 @@ public void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @N return; } } else { - // decay for stability – prevents immediate cancellation after brief jitter if (lostHoldTicks > 0) lostHoldTicks = Math.max(0, lostHoldTicks - 2); } drawTicks++; - int minHoldTicks = 5; // allow minimal wind-up before considering release + int minHoldTicks = 5; if (drawTicks >= requiredDrawTicks && drawTicks >= minHoldTicks) { boolean crosshairOn = client.crosshairTarget instanceof EntityHitResult ehr && ehr.getEntity() == target; boolean actualOk = !Double.isNaN(angleActual) && angleActual <= baseTol; @@ -183,7 +180,6 @@ private boolean canStartDrawing(ClientPlayerEntity player, LivingEntity target) .add(0, target.getStandingEyeHeight() * 0.5, 0); double pAng = computeAngleToPoint(player, predicted); if (!Double.isNaN(pAng) && pAng <= baseTol) return true; - // allow relaxed start if close to hold tolerance (helps moving targets) return !Double.isNaN(angleActual) && angleActual <= holdTol; } return !Double.isNaN(angleActual) && angleActual <= holdTol; From 13536296f1ceade1abe3c32f8bbfbb9163cb4efa Mon Sep 17 00:00:00 2001 From: AH Date: Sun, 7 Sep 2025 04:26:31 -0400 Subject: [PATCH 22/23] fix: jitter when attacking and losing course --- .../cigarette/agent/MurderMysteryAgent.java | 7 + .../cigarette/module/combat/PlayerAimbot.java | 361 ++++++++---------- 2 files changed, 165 insertions(+), 203 deletions(-) diff --git a/src/main/java/dev/cigarette/agent/MurderMysteryAgent.java b/src/main/java/dev/cigarette/agent/MurderMysteryAgent.java index c01c573b..fbbae026 100644 --- a/src/main/java/dev/cigarette/agent/MurderMysteryAgent.java +++ b/src/main/java/dev/cigarette/agent/MurderMysteryAgent.java @@ -83,6 +83,13 @@ private void createGold(ItemEntity item) { availableGold.add(gold); } + public static PersistentPlayer.Role getRole(PlayerEntity player) { + String playerName = player.getNameForScoreboard(); + PersistentPlayer persistPlayer = persistentPlayers.get(playerName); + if (persistPlayer == null) return PersistentPlayer.Role.INNOCENT; + return persistPlayer.role; + } + @Override public boolean inValidGame() { diff --git a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java index 77059636..68354bc0 100644 --- a/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java +++ b/src/main/java/dev/cigarette/module/combat/PlayerAimbot.java @@ -7,23 +7,19 @@ import dev.cigarette.gui.widget.ToggleKeybindWidget; import dev.cigarette.lib.AimingL; import dev.cigarette.lib.ServerL; +import dev.cigarette.lib.WorldL; import dev.cigarette.module.TickModule; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.AbstractClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.passive.VillagerEntity; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; -import java.util.Random; -import java.util.UUID; +import java.util.*; import java.util.stream.Stream; public class PlayerAimbot extends TickModule { @@ -32,7 +28,6 @@ public class PlayerAimbot extends TickModule { private final ToggleWidget autoAttack = new ToggleWidget("Auto Attack", "Automatically hits players").withDefaultState(true); public final SliderWidget smoothAim = new SliderWidget("Smooth Aim", "How quickly to snap to target in ticks").withBounds(1, 5, 20); public final ToggleWidget wTap = new ToggleWidget("W-Tap", "Automatically sprint before attacking").withDefaultState(false); - private final ToggleWidget testMode = new ToggleWidget("Test Mode", "Allows targeting villagers regardless of team").withDefaultState(false); public final SliderWidget attackCps = new SliderWidget("Attack CPS", "Clicks per second for auto attack").withBounds(1, 8, 15); @@ -54,22 +49,17 @@ public class PlayerAimbot extends TickModule { public final SliderWidget controlJitterScale = new SliderWidget("Control Jitter", "Randomness of control points").withBounds(0.0, 1.0, 3.0).withAccuracy(2); public final SliderWidget interpolationMode = new SliderWidget("Interpolation Mode", "0=Linear 1=Cubic 2=Cos 3=Smoothstep").withBounds(0, 1, 3).withAccuracy(0); - public final SliderWidget interferenceAngleDeg = new SliderWidget("Interference Angle (deg)", "Delta that triggers pause").withBounds(2.0, 6.0, 20.0).withAccuracy(1); - public final SliderWidget interferenceGraceTicks = new SliderWidget("Interference Grace (ticks)", "Pause length while moving").withBounds(2, 8, 30).withAccuracy(0); - public final SliderWidget aimRange = new SliderWidget("Aim Range", "Maximum range to target players").withBounds(3, 6, 20).withAccuracy(1); public final ToggleWidget prediction = new ToggleWidget("Prediction", "Predict target position").withDefaultState(false); public final SliderWidget predictionTicks = new SliderWidget("Prediction Ticks", "Ticks ahead to predict").withBounds(0, 5, 20).withAccuracy(1); - // New: minimum angular gap (deg) before initiating an aim plan & basis for dynamic prediction scaling public final SliderWidget engageAimAngle = new SliderWidget("Engage Angle (deg)", "Only begin aim plan when target angle gap >= this; also scales prediction").withBounds(1.0, 6.0, 30.0).withAccuracy(1); private PlayerAimbot(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); - this.setChildren(autoAttack, smoothAim, aimRange, prediction, predictionTicks, engageAimAngle, wTap, attackCps, ignoreTeammates, lockOnKeybind, testMode, + this.setChildren(autoAttack, smoothAim, aimRange, prediction, predictionTicks, engageAimAngle, wTap, attackCps, ignoreTeammates, lockOnKeybind, murderMysteryMode, detectiveAim, - jitterViolence, driftViolence, aimToleranceDeg, smoothingMultiplier, bezierInfluence, controlJitterScale, interpolationMode, - interferenceAngleDeg, interferenceGraceTicks); + jitterViolence, driftViolence, aimToleranceDeg, smoothingMultiplier, bezierInfluence, controlJitterScale, interpolationMode); autoAttack.registerConfigKey(id + ".autoAttack"); smoothAim.registerConfigKey(id + ".smoothAim"); aimRange.registerConfigKey(id + ".aimRange"); @@ -80,7 +70,6 @@ private PlayerAimbot(String id, String name, String tooltip) { attackCps.registerConfigKey(id + ".attackCps"); ignoreTeammates.registerConfigKey(id + ".ignoreTeammates"); lockOnKeybind.registerConfigKey(id + ".lockOnKeybind"); - testMode.registerConfigKey(id + ".testMode"); murderMysteryMode.registerConfigKey(id + ".murderMysteryMode"); detectiveAim.registerConfigKey(id + ".detectiveAim"); jitterViolence.registerConfigKey(id + ".jitterViolence"); @@ -90,8 +79,6 @@ private PlayerAimbot(String id, String name, String tooltip) { bezierInfluence.registerConfigKey(id + ".bezierInfluence"); controlJitterScale.registerConfigKey(id + ".controlJitterScale"); interpolationMode.registerConfigKey(id + ".interpolationMode"); - interferenceAngleDeg.registerConfigKey(id + ".interferenceAngleDeg"); - interferenceGraceTicks.registerConfigKey(id + ".interferenceGraceTicks"); } // Bezier aim plan state @@ -111,81 +98,43 @@ private PlayerAimbot(String id, String name, String tooltip) { // Reach assumption (entityAttackRange not exposed here) private static final double MELEE_REACH = 3.2; - private int suppressTicks = 0; private boolean lastAppliedValid = false; private float lastAppliedYaw = 0f, lastAppliedPitch = 0f; private boolean lockOnEngaged = false; + private static final float SOFT_REPLAN_ANGLE_DEG = 12f; @Override protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { if (MinecraftClient.getInstance().currentScreen != null) return; + boolean lockOnMode = lockOnKeybind.getRawState(); + if (!lockOnMode && lockOnEngaged) { lockOnEngaged = false; clearPlan(); } + LivingEntity target = null; - if (!lockOnKeybind.getRawState() && lockOnEngaged) { - lockOnEngaged = false; - clearPlan(); - } - boolean keyPressedEvent = lockOnKeybind.getKeybind().wasPressed(); - boolean keyHeld = lockOnKeybind.getKeybind().isPressed(); - LivingEntity target; - if (keyPressedEvent || keyHeld) { - if (lockOnKeybind.getRawState() && lockOnKeybind.getKeybind().wasPressed()) { + if (lockOnMode) { + if (!lockOnEngaged && lockOnKeybind.getKeybind().wasPressed()) { AbstractClientPlayerEntity best = getBestTargetFor(player); if (best != null) { - lockOnEngaged = true; - activeTarget = best; - activeTargetId = best.getUuid(); - planTicksTotal = 0; - planTicksElapsed = 0; + lockOnEngaged = true; activeTarget = best; activeTargetId = best.getUuid(); planTicksTotal = 0; planTicksElapsed = 0; } } + if (lockOnEngaged) { + if (activeTarget != null && activeTarget.isAlive() && !activeTarget.isRemoved() && player.squaredDistanceTo(activeTarget) <= Math.pow(aimRange.getRawState(), 2)) { + target = activeTarget; + } else { lockOnEngaged = false; clearPlan(); return; } + } else { clearPlan(); return; } + } else { target = pickOrValidateTarget(world, player); } - target = null; - if (handleInterferenceSuppression(player)) return; - - if (lockOnKeybind.getRawState()) { - if (lockOnEngaged) { - if (activeTarget != null && activeTarget.isAlive() && !activeTarget.isRemoved() - && player.squaredDistanceTo(activeTarget) <= Math.pow(aimRange.getRawState(), 2)) { - target = activeTarget; - } else { - lockOnEngaged = false; - clearPlan(); - return; - } - } else { - clearPlan(); - return; - } - } else { - target = pickOrValidateTarget(world, player); - } + if (target == null) { clearPlan(); return; } - if (target == null) { - clearPlan(); - return; - } - } else { - target = pickOrValidateTarget(world, player); - } - - if (target == null) { - clearPlan(); - return; - } - - // Compute raw (non-predicted) angle gap first double angleGap = computeAngleGap(player, target); double engageThresh = Math.max(0.5, engageAimAngle.getRawState()); - - // Dynamically boost prediction ticks when large angle corrections are needed int basePred = (int) Math.round(predictionTicks.getRawState()); int dynamicPred = basePred; if (prediction.getRawState() && angleGap >= engageThresh) { - // scale extra ticks up to +10 based on how many thresholds we exceed (linear) - double factor = (angleGap - engageThresh) / engageThresh; // 0 at threshold - int extra = (int) Math.min(10, Math.max(0, Math.round(factor * 4))); // each threshold multiple ~+4 ticks - dynamicPred = Math.min(30, basePred + extra); // hard cap + double factor = (angleGap - engageThresh) / engageThresh; + int extra = (int) Math.min(10, Math.max(0, Math.round(factor * 4))); + dynamicPred = Math.min(30, basePred + extra); } Vec3d aimPoint = computeAimPoint(player, target, false, dynamicPred); @@ -193,29 +142,39 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, float curYaw = getContinuousYaw(player); float curPitch = MathHelper.clamp(player.getPitch(), -90f, 90f); - boolean needPlan = !isPlanActive() || !Objects.equals(target.getUuid(), activeTargetId) || planTargetChangedSignificantly(targetAngles); - if (needPlan) { - // Only start a NEW plan if we exceed engage threshold OR no active plan yet (to allow first snap if already close) - if (angleGap >= engageThresh || !isPlanActive()) { + boolean planActive = isPlanActive(); + boolean sameTarget = Objects.equals(target.getUuid(), activeTargetId); + float dyawToEnd = Math.abs(MathHelper.wrapDegrees(endYaw - targetAngles[0])); + float dpitchToEnd = Math.abs(endPitch - targetAngles[1]); + + boolean largeChange = dyawToEnd > SOFT_REPLAN_ANGLE_DEG || dpitchToEnd > SOFT_REPLAN_ANGLE_DEG; + boolean minorChange = (dyawToEnd > 0.2f || dpitchToEnd > 0.2f) && !largeChange; + + if (!planActive || !sameTarget) { + if (angleGap >= engageThresh || !planActive) { + buildBezierPlan(curYaw, curPitch, targetAngles[0], targetAngles[1]); + activeTarget = target; activeTargetId = target.getUuid(); + } + } else { + if (largeChange) { buildBezierPlan(curYaw, curPitch, targetAngles[0], targetAngles[1]); - activeTarget = target; - activeTargetId = target.getUuid(); + } else if (minorChange) { + updatePlanEndpoint(targetAngles[0], targetAngles[1]); } } if (!isPlanActive() && angleGap < engageThresh) { - // Skip micro-adjust aiming until gap large enough - lastAppliedValid = false; // allow re-unwrap when we re-engage - return; + float snapYaw = unwrapTowards(targetAngles[0], curYaw); + player.setYaw(snapYaw); + player.setPitch(targetAngles[1]); + lastAppliedYaw = snapYaw; lastAppliedPitch = targetAngles[1]; lastAppliedValid = true; + } else { + float[] stepAngles = evalPlanStep(); + player.setYaw(stepAngles[0]); + player.setPitch(stepAngles[1]); + lastAppliedYaw = stepAngles[0]; lastAppliedPitch = stepAngles[1]; lastAppliedValid = true; } - float[] stepAngles = evalPlanStep(); - player.setYaw(stepAngles[0]); - player.setPitch(stepAngles[1]); - lastAppliedYaw = stepAngles[0]; - lastAppliedPitch = stepAngles[1]; - lastAppliedValid = true; - if (autoAttack.getRawState()) { if (ticksUntilNextAttack > 0) ticksUntilNextAttack--; boolean withinRange = player.distanceTo(target) <= MELEE_REACH; @@ -225,36 +184,52 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, Vec3d atkAim = computeAimPoint(player, target, true, dynamicPred); float[] atkAngles = AimingL.anglesFromTo(player.getEyePos(), atkAim); AimingL.lookAndAttack(world, player, target, atkAngles[0], atkAngles[1]); + buildBezierPlan(atkAngles[0], atkAngles[1], targetAngles[0], targetAngles[1]); + lastAppliedYaw = atkAngles[0]; lastAppliedPitch = atkAngles[1]; + planTicksElapsed = Math.min(1, planTicksElapsed); scheduleNextAttack(); } } } - private boolean handleInterferenceSuppression(ClientPlayerEntity player) { - float yaw = getContinuousYaw(player); - float pitch = player.getPitch(); - float angleDelta = Math.max(Math.abs(yaw - lastAppliedYaw), Math.abs(pitch - lastAppliedPitch)); - float trigger = (float) Math.max(0.5, interferenceAngleDeg.getRawState()); - - if (suppressTicks > 0) { - if (angleDelta > trigger) { - suppressTicks = (int) Math.max(1, Math.round(interferenceGraceTicks.getRawState())); - } else { - suppressTicks--; + private @Nullable LivingEntity pickOrValidateTarget(ClientWorld world, ClientPlayerEntity player) { + if (activeTarget != null && activeTarget.isAlive() && !activeTarget.isRemoved() + && player.squaredDistanceTo(activeTarget) <= Math.pow(aimRange.getRawState(), 2)) { + if (shouldFilterTeammates() && activeTarget instanceof AbstractClientPlayerEntity acp + && ServerL.playerOnSameTeam(player, acp)) { + return getBestTargetFor(player); } - lastAppliedYaw = yaw; - lastAppliedPitch = pitch; - lastAppliedValid = true; - return true; + return activeTarget; } - if (lastAppliedValid && angleDelta > trigger) { - suppressTicks = (int) Math.max(1, Math.round(interferenceGraceTicks.getRawState())); - lastAppliedYaw = yaw; - lastAppliedPitch = pitch; - return true; + return getBestTargetFor(player); + } + + private double scoreTarget(ClientPlayerEntity self, float selfYaw, AbstractClientPlayerEntity other) { + double d2 = self.squaredDistanceTo(other); + if (d2 < 0.01) d2 = 0.01; + float[] angles = AimingL.anglesFromTo(self.getEyePos(), other.getEyePos().add(0, other.getHeight() * 0.5, 0)); + float yawDiff = MathHelper.wrapDegrees(angles[0] - selfYaw); + float pitchDiff = angles[1] - self.getPitch(); + double angleDiff = Math.hypot(yawDiff, pitchDiff); + if (angleDiff < 0.1) angleDiff = 0.1; + return d2 * angleDiff; + } + + private @Nullable AbstractClientPlayerEntity detectMurderer(ClientPlayerEntity self) { + List players = self.clientWorld.getPlayers(); + Stream stream = players.stream() + .filter(p -> p != self) + .filter(p -> p.isAlive() && !p.isRemoved() && !p.isSpectator()) + .filter(p -> self.squaredDistanceTo(p) <= Math.pow(aimRange.getRawState(), 2)); + if (shouldFilterTeammates()) { + stream = stream.filter(p -> !ServerL.playerOnSameTeam(self, p)); } - return false; + return stream + .filter( + p -> MurderMysteryAgent.getRole(p) == MurderMysteryAgent.PersistentPlayer.Role.MURDERER) + .min(Comparator.comparingDouble(p -> scoreTarget(self, self.getYaw(), p))) + .orElse(null); } private void scheduleNextAttack() { @@ -265,7 +240,7 @@ private void scheduleNextAttack() { } private boolean aimAlignmentOk(ClientPlayerEntity player, LivingEntity target) { - Vec3d aimPoint = computeAimPoint(player, target, false); + Vec3d aimPoint = computeAimPoint(player, target); Vec3d lookVec = player.getRotationVec(1.0F); Vec3d toTarget = aimPoint.subtract(player.getEyePos()); if (toTarget.lengthSquared() == 0) return true; @@ -277,6 +252,15 @@ private boolean aimAlignmentOk(ClientPlayerEntity player, LivingEntity target) { return angleDeg <= tol; } + private void updatePlanEndpoint(float newYaw, float newPitch) { + endYaw = unwrapTowards(newYaw, startYaw); + endPitch = MathHelper.clamp(newPitch, -90f, 90f); + float minPitch = Math.min(startPitch, endPitch); + float maxPitch = Math.max(startPitch, endPitch); + c1Pitch = MathHelper.clamp(c1Pitch, minPitch, maxPitch); + c2Pitch = MathHelper.clamp(c2Pitch, minPitch, maxPitch); + } + private float[] evalPlanStep() { if (!isPlanActive()) return new float[]{startYaw, startPitch}; planTicksElapsed = Math.min(planTicksElapsed + 1, planTicksTotal); @@ -291,28 +275,36 @@ private float[] evalPlanStep() { float pitch = linPitch + (bezPitch - linPitch) * inf; float angleSpan = Math.abs(MathHelper.wrapDegrees(startYaw - endYaw)) + Math.abs(endPitch - startPitch); - float baseJitter = Math.max(0.0f, Math.min(0.5f, angleSpan * 0.003f)); - float jitterMag = (float) (baseJitter * Math.max(0.0, jitterViolence.getRawState())); - float driftMag = (float) (jitterMag * Math.max(0.0, driftViolence.getRawState())); - float fineJitterYaw = (float) (rng.nextGaussian() * jitterMag * (1.0 - et)); - float fineJitterPitch = (float) (rng.nextGaussian() * jitterMag * (1.0 - et)); - float driftYaw = (float) (rng.nextGaussian() * driftMag * 0.2); - float driftPitch = (float) (rng.nextGaussian() * driftMag * 0.2); + float baseJitter = Math.max(0.0f, Math.min(0.4f, angleSpan * 0.0025f)); + float jitterScale = (float) Math.max(0.0, jitterViolence.getRawState()); + float driftScale = (float) Math.max(0.0, driftViolence.getRawState()); + float jitterMagYaw = baseJitter * jitterScale; + float jitterMagPitch = baseJitter * 0.55f * jitterScale; + float driftMagYaw = jitterMagYaw * 0.5f * driftScale; + float driftMagPitch = jitterMagPitch * 0.5f * driftScale; + float fineJitterYaw = (float) (rng.nextGaussian() * jitterMagYaw * (1.0 - et)); + float fineJitterPitch = (float) (rng.nextGaussian() * jitterMagPitch * (1.0 - et)); + float driftYaw = (float) (rng.nextGaussian() * driftMagYaw * 0.2); + float driftPitch = (float) (rng.nextGaussian() * driftMagPitch * 0.2); float outYaw = yaw + fineJitterYaw + driftYaw; - float outPitch = MathHelper.clamp(pitch + fineJitterPitch + driftPitch, -90f, 90f); + float outPitch = pitch + fineJitterPitch + driftPitch; + float minBand = Math.min(startPitch, endPitch) - 2.0f; + float maxBand = Math.max(startPitch, endPitch) + 2.0f; + outPitch = MathHelper.clamp(outPitch, minBand, maxBand); + outPitch = MathHelper.clamp(outPitch, -90f, 90f); return new float[]{outYaw, outPitch}; } private void buildBezierPlan(float curYaw, float curPitch, float targetYaw, float targetPitch) { - float tYaw = unwrapTowards(targetYaw, curYaw); startYaw = curYaw; startPitch = curPitch; + float tYaw = unwrapTowards(targetYaw, curYaw); endYaw = tYaw; endPitch = MathHelper.clamp(targetPitch, -90f, 90f); - float yawDelta = tYaw - curYaw; - float pitchDelta = endPitch - curPitch; + float yawDelta = endYaw - startYaw; + float pitchDelta = endPitch - startPitch; float angleMag = Math.abs(yawDelta) + Math.abs(pitchDelta); int baseTicks = (int) Math.max(1, Math.round(smoothAim.getRawState())); @@ -323,10 +315,15 @@ private void buildBezierPlan(float curYaw, float curPitch, float targetYaw, floa float c1t = 0.30f + (float) rng.nextDouble() * 0.15f; float c2t = 0.70f - (float) rng.nextDouble() * 0.15f; float ctrlJitter = (float) (Math.max(0.0f, Math.min(3.5f, angleMag * 0.05f)) * Math.max(0.0, controlJitterScale.getRawState())); - c1Yaw = curYaw + yawDelta * c1t + (float) (rng.nextGaussian() * ctrlJitter); - c2Yaw = curYaw + yawDelta * c2t + (float) (rng.nextGaussian() * ctrlJitter); - c1Pitch = curPitch + pitchDelta * c1t + (float) (rng.nextGaussian() * ctrlJitter * 0.6f); - c2Pitch = curPitch + pitchDelta * c2t + (float) (rng.nextGaussian() * ctrlJitter * 0.6f); + c1Yaw = startYaw + yawDelta * c1t + (float) (rng.nextGaussian() * ctrlJitter); + c2Yaw = startYaw + yawDelta * c2t + (float) (rng.nextGaussian() * ctrlJitter); + c1Pitch = startPitch + pitchDelta * c1t + (float) (rng.nextGaussian() * ctrlJitter * 0.6f); + c2Pitch = startPitch + pitchDelta * c2t + (float) (rng.nextGaussian() * ctrlJitter * 0.6f); + + float minPitch = Math.min(startPitch, endPitch); + float maxPitch = Math.max(startPitch, endPitch); + c1Pitch = MathHelper.clamp(c1Pitch, minPitch, maxPitch); + c2Pitch = MathHelper.clamp(c2Pitch, minPitch, maxPitch); c1Yaw = unwrapTowards(c1Yaw, startYaw); c2Yaw = unwrapTowards(c2Yaw, startYaw); @@ -402,16 +399,31 @@ private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, bo if (prediction.getRawState()) { Vec3d vel = target.getVelocity(); int ticks = Math.max(0, dynamicTicks); - Vec3d predictedPos = target.getPos().add(vel.multiply(ticks)); - return predictedPos.add(0, target.getHeight() * 0.5, 0); + double px = target.getX() + vel.x * ticks; + double pz = target.getZ() + vel.z * ticks; + + double baseY = target.getY(); + double height = target.getHeight(); + double py; + if (target.isOnGround() || Math.abs(vel.y) < 0.05 || ticks == 0) { + py = baseY + height * 0.5; + } else { + double g = 0.08; + double vy = vel.y; + double t = ticks; + double predictedY = baseY + (vy * t) - 0.5 * g * t * t; + double minY = baseY + height * 0.25; + double maxY = baseY + height * 0.85; + py = MathHelper.clamp(predictedY, minY, maxY); + } + return new Vec3d(px, py, pz); } else { return AimingL.getAimPointInsideHitbox(player, target, attackNow, 0.1, 0.6, 2.5); } } - // Retain original signature for any other callers; delegate to dynamic with base ticks - private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target, boolean attackNow) { - return computeAimPoint(player, target, attackNow, (int) Math.round(predictionTicks.getRawState())); + private Vec3d computeAimPoint(ClientPlayerEntity player, LivingEntity target) { + return computeAimPoint(player, target, false, (int) Math.round(predictionTicks.getRawState())); } private boolean isMurderMysteryActive() { @@ -422,83 +434,23 @@ private boolean shouldFilterTeammates() { return !isMurderMysteryActive() && ignoreTeammates.getRawState(); } - private @Nullable AbstractClientPlayerEntity detectMurderer(@NotNull ClientPlayerEntity self) { - if (!isMurderMysteryActive()) return null; - float curYaw = getContinuousYaw(self); - return MurderMysteryAgent.getVisiblePlayers().stream() - .filter(pp -> pp != null && pp.playerEntity != null && pp.playerEntity.isAlive()) - .filter(pp -> pp.role == MurderMysteryAgent.PersistentPlayer.Role.MURDERER) - .map(pp -> pp.playerEntity) - .filter(pe -> pe != self) - .filter(pe -> self.squaredDistanceTo(pe) <= Math.pow(aimRange.getRawState(), 2)) - .filter(pe -> pe instanceof AbstractClientPlayerEntity) - .map(pe -> (AbstractClientPlayerEntity) pe) - .min(Comparator.comparingDouble(p -> scoreTarget(self, curYaw, p))) - .orElse(null); - } - - private @Nullable AbstractClientPlayerEntity detectDetective(@NotNull ClientPlayerEntity self) { - if (!isMurderMysteryActive() || !detectiveAim.getRawState()) return null; - float curYaw = getContinuousYaw(self); - return MurderMysteryAgent.getVisiblePlayers().stream() - .filter(pp -> pp != null && pp.playerEntity != null && pp.playerEntity.isAlive()) - .filter(pp -> pp.role == MurderMysteryAgent.PersistentPlayer.Role.DETECTIVE) - .map(pp -> pp.playerEntity) - .filter(pe -> pe != self) - .filter(pe -> self.squaredDistanceTo(pe) <= Math.pow(aimRange.getRawState(), 2)) - .filter(pe -> pe instanceof AbstractClientPlayerEntity) - .map(pe -> (AbstractClientPlayerEntity) pe) - .min(Comparator.comparingDouble(p -> scoreTarget(self, curYaw, p))) - .orElse(null); - } - - private @Nullable LivingEntity pickOrValidateTarget(ClientWorld world, ClientPlayerEntity player) { - if (isMurderMysteryActive()) { - AbstractClientPlayerEntity mm = detectMurderer(player); - if (mm != null) return mm; - AbstractClientPlayerEntity det = detectDetective(player); - if (det != null) return det; - return null; - } - - if (activeTarget != null && activeTarget.isAlive() && !activeTarget.isRemoved()) { - if (activeTarget.squaredDistanceTo(player) <= (MELEE_REACH + 4.0) * (MELEE_REACH + 4.0)) { - if (!(activeTarget instanceof AbstractClientPlayerEntity p) || !shouldFilterTeammates() || !ServerL.playerOnSameTeam(player, p)) { - return activeTarget; - } - } - } - - Stream players = world.getPlayers().stream() - .filter(p -> p != player) + private AbstractClientPlayerEntity detectDetective(ClientPlayerEntity self) { + if (!detectiveAim.getRawState()) return null; + List players = self.clientWorld.getPlayers(); + Stream stream = players.stream() + .filter(p -> p != self) .filter(p -> p.isAlive() && !p.isRemoved() && !p.isSpectator()) - .filter(p -> !shouldFilterTeammates() || !ServerL.playerOnSameTeam(player, p)) - .map(p -> (LivingEntity) p); - - Stream villagers = Stream.empty(); - if (testMode.getRawState()) { - List vs = world.getEntitiesByClass(VillagerEntity.class, player.getBoundingBox().expand(12.0), v -> v.isAlive() && !v.isRemoved()); - villagers = vs.stream().map(v -> (LivingEntity) v); + .filter(p -> self.squaredDistanceTo(p) <= Math.pow(aimRange.getRawState(), 2)); + if (shouldFilterTeammates()) { + stream = stream.filter(p -> !ServerL.playerOnSameTeam(self, p)); } - - float curYaw = getContinuousYaw(player); - return Stream.concat(players, villagers) - .sorted(Comparator.comparingDouble(player::squaredDistanceTo)) - .filter(e -> player.squaredDistanceTo(e) <= Math.pow(aimRange.getRawState(), 2)) - .min(Comparator.comparingDouble(e -> scoreTarget(player, curYaw, e))) + return stream + .filter( + p -> MurderMysteryAgent.getRole(p) == MurderMysteryAgent.PersistentPlayer.Role.DETECTIVE) + .min(Comparator.comparingDouble(p -> scoreTarget(self, self.getYaw(), p))) .orElse(null); } - private double scoreTarget(ClientPlayerEntity player, float curYaw, LivingEntity e) { - double dist = player.distanceTo(e); - double dx = e.getX() - player.getX(); - double dz = e.getZ() - player.getZ(); - double targetYaw = Math.toDegrees(Math.atan2(-dx, dz)); - double ang = Math.abs(MathHelper.wrapDegrees((float) (curYaw - targetYaw))); - double behindPenalty = ang > 120 ? 1000 : 0; - return dist + ang * 0.02 + behindPenalty; - } - public static @Nullable AbstractClientPlayerEntity getBestTargetFor(ClientPlayerEntity self) { if (self == null || self.clientWorld == null) return null; ClientWorld world = self.clientWorld; @@ -516,13 +468,16 @@ private double scoreTarget(ClientPlayerEntity player, float curYaw, LivingEntity boolean filterTeams = !INSTANCE.isMurderMysteryActive() && INSTANCE.ignoreTeammates.getRawState(); return world.getPlayers().stream() .filter(p -> p != self) - .filter(p -> p.isAlive() && !p.isRemoved() && !p.isSpectator()) .filter(p -> self.squaredDistanceTo(p) <= range2) .filter(p -> !filterTeams || !ServerL.playerOnSameTeam(self, p)) + .filter(botPredicate) .min(Comparator.comparingDouble(p -> INSTANCE.scoreTarget(self, curYaw, p))) .orElse(null); } + public static final java.util.function.Predicate botPredicate = p -> + !(p.isSleeping() || p.isSpectator() || p.isInvisible() || p.isDead() || p.age < 20 || !WorldL.isRealPlayer(p)); + @Override protected void onDisabledTick(MinecraftClient client) { clearPlan(); From d445dff5932ed2bce75b0231ecd755871b26fca2 Mon Sep 17 00:00:00 2001 From: microcrit <102484505+microcrit@users.noreply.github.com> Date: Thu, 9 Oct 2025 22:32:56 -0400 Subject: [PATCH 23/23] Merge --- src/main/java/io/github/waqfs/gui/CigaretteScreen.java | 0 .../java/io/github/waqfs/gui/hud/modules/ModuleListDisplay.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/java/io/github/waqfs/gui/CigaretteScreen.java create mode 100644 src/main/java/io/github/waqfs/gui/hud/modules/ModuleListDisplay.java diff --git a/src/main/java/io/github/waqfs/gui/CigaretteScreen.java b/src/main/java/io/github/waqfs/gui/CigaretteScreen.java new file mode 100644 index 00000000..e69de29b diff --git a/src/main/java/io/github/waqfs/gui/hud/modules/ModuleListDisplay.java b/src/main/java/io/github/waqfs/gui/hud/modules/ModuleListDisplay.java new file mode 100644 index 00000000..e69de29b