From 4d16c1f7a1ac08185db186b0a9574536e5bd5162 Mon Sep 17 00:00:00 2001 From: Redstone1024 <2824517378@qq.com> Date: Wed, 28 Aug 2024 13:24:24 +0800 Subject: [PATCH] Add probe to try to attack the entity behind the block --- .../com/example/addon/modules/Prediction.java | 428 ++++++++++++------ 1 file changed, 288 insertions(+), 140 deletions(-) diff --git a/src/main/java/com/example/addon/modules/Prediction.java b/src/main/java/com/example/addon/modules/Prediction.java index b53cf31..2de6572 100644 --- a/src/main/java/com/example/addon/modules/Prediction.java +++ b/src/main/java/com/example/addon/modules/Prediction.java @@ -23,14 +23,12 @@ import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.passive.AnimalEntity; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.projectile.ProjectileUtil; import net.minecraft.fluid.FluidState; import net.minecraft.fluid.Fluids; import net.minecraft.item.BowItem; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.*; import net.minecraft.world.RaycastContext; @@ -38,11 +36,14 @@ import org.joml.Vector3d; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Set; public class Prediction extends Module { private final SettingGroup sgGeneral = settings.getDefaultGroup(); private final SettingGroup sgTarget = settings.createGroup("Target"); + private final SettingGroup sgSimulation = settings.createGroup("Simulation"); + private final SettingGroup sgProbe = settings.createGroup("Probe"); private final SettingGroup sgSpeed = settings.createGroup("Aim Speed"); private final SettingGroup sgRender = settings.createGroup("Render"); @@ -55,14 +56,6 @@ public class Prediction extends Module { .build() ); - public final Setting simulationSteps = sgGeneral.add(new IntSetting.Builder() - .name("simulation-steps") - .description("How many steps to simulate projectiles. Zero for no limit.") - .defaultValue(250) - .sliderMax(5000) - .build() - ); - public final Setting iterationSteps = sgGeneral.add(new IntSetting.Builder() .name("iteration-steps") .description("How many iterations to aim projectiles. Zero for no limit.") @@ -77,7 +70,7 @@ public class Prediction extends Module { .description("How much accuracy is needed to aim a projectile.") .defaultValue(0.5) .sliderMax(1) - .visible(() -> iterationSteps.get() == 0) + .visible(() -> predictionLevel.get() >= 1 && iterationSteps.get() == 0) .build() ); @@ -148,6 +141,78 @@ public class Prediction extends Module { .build() ); + public final Setting simulationSteps = sgSimulation.add(new IntSetting.Builder() + .name("simulation-steps") + .description("How many steps to simulate projectiles. Zero for no limit.") + .defaultValue(250) + .sliderMax(5000) + .build() + ); + + public final Setting eyeHeightOffset = sgSimulation.add(new DoubleSetting.Builder() + .name("eye-height-offset") + .description("What is the offset of the actual projectile launch position from eye height.") + .defaultValue(-0.1) + .sliderMin(-1.0) + .sliderMax(1.0) + .build() + ); + + public final Setting gravity = sgSimulation.add(new DoubleSetting.Builder() + .name("gravity") + .description("What is the acceleration of gravity on the projectile.") + .defaultValue(0.05000000074505806) + .sliderMin(0.04) + .sliderMax(0.06) + .build() + ); + + public final Setting airDrag = sgSimulation.add(new DoubleSetting.Builder() + .name("air-drag") + .description("What is the drag value of the projectile in air.") + .defaultValue(0.99) + .sliderMin(0.95) + .sliderMax(1.0) + .build() + ); + + public final Setting waterDrag = sgSimulation.add(new DoubleSetting.Builder() + .name("water-drag") + .description("What is the drag value of the projectile in water.") + .defaultValue(0.6) + .sliderMin(0) + .sliderMax(1) + .build() + ); + + private final Setting enableProbe = sgProbe.add(new BoolSetting.Builder() + .name("enable-probe") + .description("Enable/disable block probes.") + .defaultValue(false) + .visible(() -> predictionLevel.get() >= 1) + .build() + ); + + private final Setting probeRange = sgProbe.add(new DoubleSetting.Builder() + .name("probe-range") + .description("The maximum range of probe placement.") + .defaultValue(1) + .range(0, 4) + .sliderMax(4) + .visible(enableProbe::get) + .build() + ); + + private final Setting probeInterval = sgProbe.add(new DoubleSetting.Builder() + .name("probe-interval") + .description("The minimum interval of probe placement.") + .defaultValue(0.5) + .range(0, 1) + .sliderMax(1) + .visible(enableProbe::get) + .build() + ); + private final Setting instant = sgSpeed.add(new BoolSetting.Builder() .name("instant-look") .description("Instantly looks at the entity.") @@ -166,7 +231,7 @@ public class Prediction extends Module { private final Setting enableRender = sgRender.add(new BoolSetting.Builder() .name("enable-render") - .description("Enable/Disable Render Indication.") + .description("Enable/disable render indication.") .defaultValue(true) .build() ); @@ -220,11 +285,8 @@ public class Prediction extends Module { @EventHandler private void onTick(TickEvent.Post event) { - if (mc.options.attackKey.isPressed() || !isSelectableTarget(targetEntity)) { - targetEntity = TargetUtils.get(this::isSelectableTarget, priority.get()); - } - clearPath(); + calculateTarget(); if (targetEntity != null) calculateAngle(); } @@ -232,9 +294,7 @@ public class Prediction extends Module { private void onRender(Render3DEvent event) { float tickDelta = mc.world.getTickManager().isFrozen() ? 1 : event.tickDelta; - boolean canAim = aimAssist.get() && (isHitTarget || !aimOnlyHit.get()) && mc.options.useKey.isPressed() && InvUtils.testInHands(Items.BOW); - - if (canAim) aim(event.tickDelta); + if (aimAssist.get()) aim(event.tickDelta); if (enableRender.get()) renderPath(event); } @@ -268,29 +328,22 @@ public class Prediction extends Module { return BowItem.getPullProgress(mc.player.getItemUseTime()); } - private static final double gravity = 0.05000000074505806; - private static final double airDrag = 0.99; - private static final double waterDrag = 0.6; - - // These variables are set by the onTick() + // These variables are set by the calculateTarget() private Entity targetEntity; + private Box targetBox; // These variables are set by the calculateAngle() + private boolean targetCompleted; private double targetYaw; private double targetPitch; private double targetCharge; - // These variables are set by the calculatePath() for prediction level >= 1 - private double targetHighPitch; - private double targetLowPitch; - // These variables are set by the calculatePath() private final Pool vectorPool = new Pool<>(Vector3d::new); private final List points = new ArrayList<>(); private boolean hitQuad = false, hitQuadHorizontal = false; private final Vector3d hitQuad1 = new Vector3d(); private final Vector3d hitQuad2 = new Vector3d(); - private Entity collidingEntity = null; private boolean isHitTarget; private void clearPath() { @@ -298,7 +351,6 @@ public class Prediction extends Module { points.clear(); hitQuad = false; - collidingEntity = null; isHitTarget = false; } @@ -310,7 +362,7 @@ public class Prediction extends Module { Vector3d lastPosition = new Vector3d(0.0, 0.0, 0.0); Vector3d position = new Vector3d(0.0, 0.0, 0.0); Vector3d velocity = new Vector3d(0.0, 0.0, 0.0); - position.set(mc.player.getX(), mc.player.getY(), mc.player.getZ()).add(0, mc.player.getEyeHeight(mc.player.getPose()), 0); + position.set(mc.player.getX(), mc.player.getY(), mc.player.getZ()).add(0, mc.player.getEyeHeight(mc.player.getPose()) + eyeHeightOffset.get(), 0); double yaw = targetYaw; double pitch = targetPitch; @@ -323,8 +375,9 @@ public class Prediction extends Module { velocity.add(mc.player.getVelocity().x, mc.player.isOnGround() ? 0.0D : mc.player.getVelocity().y, mc.player.getVelocity().z); HitResult hitResult = null; + Direction hitDirection = null; - for (int i = 0; i < (simulationSteps.get() > 0 ? simulationSteps.get() : Integer.MAX_VALUE) && hitResult == null; i++) { + for (int i = 0; i < (simulationSteps.get() > 0 ? simulationSteps.get() : Integer.MAX_VALUE) && !hitQuad && !isHitTarget; i++) { points.add(vectorPool.get().set(position)); lastPosition.set(position); @@ -335,8 +388,8 @@ public class Prediction extends Module { if (fluidState.getFluid() == Fluids.WATER || fluidState.getFluid() == Fluids.FLOWING_WATER) isTouchingWater = position.y - (int) position.y <= fluidState.getHeight(); - velocity.mul(isTouchingWater ? waterDrag : airDrag); - velocity.sub(0, gravity, 0); + velocity.mul(isTouchingWater ? waterDrag.get() : airDrag.get()); + velocity.sub(0, gravity.get(), 0); if (position.y < mc.world.getBottomY()) { hitResult = MissHitResult.INSTANCE; @@ -356,64 +409,94 @@ public class Prediction extends Module { ); if (hitResult.getType() != HitResult.Type.MISS) { position = new Vector3d(hitResult.getPos().x, hitResult.getPos().y, hitResult.getPos().z); + hitQuad = true; + hitQuad1.set(position); + hitQuad2.set(position); + hitDirection = ((BlockHitResult) hitResult).getSide(); } - Box box = new Box( - lastPosition.x - (EntityType.ARROW.getWidth() / 2f), - lastPosition.y, - lastPosition.z - (EntityType.ARROW.getWidth() / 2f), - lastPosition.x + (EntityType.ARROW.getWidth() / 2f), - lastPosition.y + EntityType.ARROW.getHeight(), - lastPosition.z + (EntityType.ARROW.getWidth() / 2f) - ).stretch(velocity.x, velocity.y, velocity.z).expand(1.0D); - HitResult hitResultEntity = ProjectileUtil.getEntityCollision(mc.world, null, - new Vec3d(lastPosition.x, lastPosition.y, lastPosition.z), new Vec3d(position.x, position.y, position.z), - box, entity -> !entity.isSpectator() && entity.isAlive() && entity.canHit() && entity != mc.player - ); - if (hitResultEntity != null) { - hitResult = hitResultEntity; + if (!targetBox.stretch(velocity.x, velocity.y, velocity.z).expand(4.0).contains(position.x, position.y, position.z)) + continue; + + Box extendedBox = targetBox; + + if (enableProbe.get()) { + hitResult = mc.world.raycast(new RaycastContext( + targetBox.getCenter(), targetBox.getCenter().add(-(targetBox.getLengthX() + EntityType.ARROW.getWidth()) / 2.0, 0, 0), + RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, mc.player) + ); + + double extendedMinX = hitResult.getType() != HitResult.Type.MISS ? hitResult.getPos().x : targetBox.minX - EntityType.ARROW.getWidth() / 2.0; + + hitResult = mc.world.raycast(new RaycastContext( + targetBox.getCenter(), targetBox.getCenter().add(0, -(targetBox.getLengthY() + EntityType.ARROW.getHeight()) / 2.0, 0), + RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, mc.player) + ); + + double extendedMinY = hitResult.getType() != HitResult.Type.MISS ? hitResult.getPos().y : targetBox.minY - EntityType.ARROW.getHeight() / 2.0; + + hitResult = mc.world.raycast(new RaycastContext( + targetBox.getCenter(), targetBox.getCenter().add(0, 0, -(targetBox.getLengthZ() + EntityType.ARROW.getWidth()) / 2.0), + RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, mc.player) + ); + + double extendedMinZ = hitResult.getType() != HitResult.Type.MISS ? hitResult.getPos().z : targetBox.minZ - EntityType.ARROW.getWidth() / 2.0; + + hitResult = mc.world.raycast(new RaycastContext( + targetBox.getCenter(), targetBox.getCenter().add((targetBox.getLengthX() + EntityType.ARROW.getWidth()) / 2.0, 0, 0), + RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, mc.player) + ); + + double extendedMaxX = hitResult.getType() != HitResult.Type.MISS ? hitResult.getPos().x : targetBox.maxX + EntityType.ARROW.getWidth() / 2.0; + + hitResult = mc.world.raycast(new RaycastContext( + targetBox.getCenter(), targetBox.getCenter().add(0, (targetBox.getLengthY() + EntityType.ARROW.getHeight()) / 2.0, 0), + RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, mc.player) + ); + + double extendedMaxY = hitResult.getType() != HitResult.Type.MISS ? hitResult.getPos().y : targetBox.maxY + EntityType.ARROW.getHeight() / 2.0; + + hitResult = mc.world.raycast(new RaycastContext( + targetBox.getCenter(), targetBox.getCenter().add(0, 0, (targetBox.getLengthZ() + EntityType.ARROW.getWidth()) / 2.0), + RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, mc.player) + ); + + double extendedMaxZ = hitResult.getType() != HitResult.Type.MISS ? hitResult.getPos().z : targetBox.maxZ + EntityType.ARROW.getWidth() / 2.0; + + extendedBox = new Box(extendedMinX, extendedMinY, extendedMinZ, extendedMaxX, extendedMaxY, extendedMaxZ); } - hitResult = hitResult.getType() == HitResult.Type.MISS ? null : hitResult; + Optional optional = extendedBox.raycast(new Vec3d(lastPosition.x, lastPosition.y, lastPosition.z), new Vec3d(position.x, position.y, position.z)); + + if (optional.isPresent()) { + isHitTarget = true; + } } - if (hitResult != null) { - if (hitResult.getType() == HitResult.Type.BLOCK) { - BlockHitResult r = (BlockHitResult) hitResult; - - hitQuad = true; - hitQuad1.set(r.getPos().x, r.getPos().y, r.getPos().z); - hitQuad2.set(r.getPos().x, r.getPos().y, r.getPos().z); - - if (r.getSide() == Direction.UP || r.getSide() == Direction.DOWN) { - hitQuadHorizontal = true; - hitQuad1.x -= 0.25; - hitQuad1.z -= 0.25; - hitQuad2.x += 0.25; - hitQuad2.z += 0.25; - } - else if (r.getSide() == Direction.NORTH || r.getSide() == Direction.SOUTH) { - hitQuadHorizontal = false; - hitQuad1.x -= 0.25; - hitQuad1.y -= 0.25; - hitQuad2.x += 0.25; - hitQuad2.y += 0.25; - } - else { - hitQuadHorizontal = false; - hitQuad1.z -= 0.25; - hitQuad1.y -= 0.25; - hitQuad2.z += 0.25; - hitQuad2.y += 0.25; - } - - points.add(Utils.set(vectorPool.get(), hitResult.getPos())); + if (hitQuad) { + if (hitDirection == Direction.UP || hitDirection == Direction.DOWN) { + hitQuadHorizontal = true; + hitQuad1.x -= 0.25; + hitQuad1.z -= 0.25; + hitQuad2.x += 0.25; + hitQuad2.z += 0.25; } - else if (hitResult.getType() == HitResult.Type.ENTITY) { - collidingEntity = ((EntityHitResult) hitResult).getEntity(); - points.add(Utils.set(vectorPool.get(), hitResult.getPos()).add(0, collidingEntity.getHeight() / 2, 0)); - isHitTarget = collidingEntity == targetEntity; + else if (hitDirection == Direction.NORTH || hitDirection == Direction.SOUTH) { + hitQuadHorizontal = false; + hitQuad1.x -= 0.25; + hitQuad1.y -= 0.25; + hitQuad2.x += 0.25; + hitQuad2.y += 0.25; } + else { + hitQuadHorizontal = false; + hitQuad1.z -= 0.25; + hitQuad1.y -= 0.25; + hitQuad2.z += 0.25; + hitQuad2.y += 0.25; + } + + points.add(Utils.set(vectorPool.get(), hitResult.getPos())); } } @@ -448,37 +531,31 @@ public class Prediction extends Module { } } - if (collidingEntity != null) { - double x = (collidingEntity.getX() - collidingEntity.prevX) * event.tickDelta; - double y = (collidingEntity.getY() - collidingEntity.prevY) * event.tickDelta; - double z = (collidingEntity.getZ() - collidingEntity.prevZ) * event.tickDelta; - - Box box = collidingEntity.getBoundingBox(); + if (targetEntity != null) { + Box box = targetEntity.getBoundingBox(); event.renderer.box( - x + box.minX, y + box.minY, z + box.minZ, x + box.maxX, y + box.maxY, z + box.maxZ, + targetBox, isHitTarget ? hitSideColor.get() : missSideColor.get(), isHitTarget ? hitLineColor.get() : missLineColor.get(), shapeMode.get(), 0 ); } - - if (!isHitTarget && targetEntity != null) { - double x = (targetEntity.getX() - targetEntity.prevX) * event.tickDelta; - double y = (targetEntity.getY() - targetEntity.prevY) * event.tickDelta; - double z = (targetEntity.getZ() - targetEntity.prevZ) * event.tickDelta; - - Box box = targetEntity.getBoundingBox(); - event.renderer.box( - x + box.minX, y + box.minY, z + box.minZ, x + box.maxX, y + box.maxY, z + box.maxZ, - missSideColor.get(), missLineColor.get(), shapeMode.get(), 0 - ); - } } - private double calculateHeightOffset(Vector3d targetPosition, double pitch) { + private void calculateTarget() { + if (mc.options.attackKey.isPressed() || !isSelectableTarget(targetEntity)) { + targetEntity = TargetUtils.get(this::isSelectableTarget, priority.get()); + } + + if (targetEntity == null) return; + + targetBox = targetEntity.getBoundingBox(); + } + + private double calculateHeightOffset(double pitch) { double targetDistance = Math.sqrt( - (targetPosition.x - mc.player.getX()) * (targetPosition.x - mc.player.getX()) + - (targetPosition.z - mc.player.getZ()) * (targetPosition.z - mc.player.getZ()) + (targetBox.getCenter().x - mc.player.getX()) * (targetBox.getCenter().x - mc.player.getX()) + + (targetBox.getCenter().z - mc.player.getZ()) * (targetBox.getCenter().z - mc.player.getZ()) ); if (getCurrentCharge() <= 0) return Double.NaN; @@ -487,9 +564,9 @@ public class Prediction extends Module { Vector3d lastPosition = new Vector3d(0.0, 0.0, 0.0); Vector3d position = new Vector3d(0.0, 0.0, 0.0); Vector3d velocity = new Vector3d(0.0, 0.0, 0.0); - position.set(mc.player.getX(), mc.player.getY(), mc.player.getZ()).add(0, mc.player.getEyeHeight(mc.player.getPose()), 0); + position.set(mc.player.getX(), mc.player.getY(), mc.player.getZ()).add(0, mc.player.getEyeHeight(mc.player.getPose()) + eyeHeightOffset.get(), 0); - double yaw = Rotations.getYaw(new Vec3d(targetPosition.x, targetPosition.y, targetPosition.z)); + double yaw = Rotations.getYaw(new Vec3d(targetBox.getCenter().x, targetBox.getCenter().y, targetBox.getCenter().z)); double x = -Math.sin(yaw * 0.017453292) * Math.cos(pitch * 0.017453292); double y = -Math.sin(pitch * 0.017453292); @@ -507,8 +584,8 @@ public class Prediction extends Module { if (fluidState.getFluid() == Fluids.WATER || fluidState.getFluid() == Fluids.FLOWING_WATER) isTouchingWater = position.y - (int) position.y <= fluidState.getHeight(); - velocity.mul(isTouchingWater ? waterDrag : airDrag); - velocity.sub(0, gravity, 0); + velocity.mul(isTouchingWater ? waterDrag.get() : airDrag.get()); + velocity.sub(0, gravity.get(), 0); if (position.y < mc.world.getBottomY()) return Double.NEGATIVE_INFINITY; @@ -523,7 +600,7 @@ public class Prediction extends Module { ); if (distance > targetDistance) { - return MathHelper.lerp((targetDistance - laseDistance) / (distance - laseDistance), lastPosition.y, position.y) - targetPosition.y; + return MathHelper.lerp((targetDistance - laseDistance) / (distance - laseDistance), lastPosition.y, position.y) - targetBox.getCenter().y; } } @@ -533,6 +610,8 @@ public class Prediction extends Module { private void calculateAngle() { if (targetEntity == null) return; + targetCompleted = false; + // parabolic prediction if (predictionLevel.get() == 0) { double posX = targetEntity.getPos().getX(); @@ -559,42 +638,43 @@ public class Prediction extends Module { } targetCharge = 1.0; + targetCompleted = true; calculatePath(); return; } - Vector3d targetPosition = new Vector3d(targetEntity.getX(), targetEntity.getY() + targetEntity.getHeight() / 2.0, targetEntity.getZ()); + double highestPitch = 0.0; + boolean findPitch = false; // Basic physics prediction - if (predictionLevel.get() == 1) { + if (predictionLevel.get() >= 1) { // Solve for the highest pitch - double highestPitch; { double minPitch = -90.0; double maxPitch = 90.0; for (int i = 0; iterationSteps.get() > 0 ? i < iterationSteps.get() : maxPitch - minPitch > iterationEpsilon.get(); i++) { double mid1 = minPitch + (maxPitch - minPitch) / 3.0; double mid2 = maxPitch - (maxPitch - minPitch) / 3.0; - double mid1Height = calculateHeightOffset(targetPosition, mid1); - double mid2Height = calculateHeightOffset(targetPosition, mid2); + double mid1Height = calculateHeightOffset(mid1); + double mid2Height = calculateHeightOffset(mid2); if (Double.isNaN(mid1Height) || Double.isNaN(mid2Height)) return; if (mid1Height < mid2Height) minPitch = mid1; else maxPitch = mid2; } highestPitch = (minPitch + maxPitch) / 2.0; - if (calculateHeightOffset(targetPosition, highestPitch) < 0.0) return; + if (calculateHeightOffset(highestPitch) < 0.0) return; } // Solve for the low pitch + double targetLowPitch = highestPitch; { - targetLowPitch = highestPitch; double minPitch = highestPitch; double maxPitch = 90.0; for (int i = 0; iterationSteps.get() > 0 ? i < iterationSteps.get() : maxPitch - minPitch > iterationEpsilon.get(); i++) { double mid = (minPitch + maxPitch) / 2.0; - double midHeight = calculateHeightOffset(targetPosition, mid); + double midHeight = calculateHeightOffset(mid); if (Double.isNaN(midHeight)) { targetLowPitch = Double.NaN; break; @@ -606,48 +686,116 @@ public class Prediction extends Module { if (!Double.isNaN(targetLowPitch)) targetLowPitch = (minPitch + maxPitch) / 2.0; } - if (!Double.isNaN(targetLowPitch)) - { + if (!Double.isNaN(targetLowPitch)) { + findPitch = true; + targetPitch = targetLowPitch; targetYaw = Rotations.getYaw(targetEntity); targetCharge = getCurrentCharge(); + targetCompleted = true; calculatePath(); } + } - // Solve for the high pitch - if (!isHitTarget && allowHighThrows.get()) { - targetHighPitch = highestPitch; - double minPitch = -90.0; - double maxPitch = highestPitch; - for (int i = 0; iterationSteps.get() > 0 ? i < iterationSteps.get() : maxPitch - minPitch > iterationEpsilon.get(); i++) { - double mid = (minPitch + maxPitch) / 2.0; - double midHeight = calculateHeightOffset(targetPosition, mid); - if (Double.isNaN(midHeight)) { - targetHighPitch = Double.NaN; - break; - } - if (midHeight <= 0) - minPitch = mid; - else maxPitch = mid; + // If you can't hit because a block is blocking, try to use a probe. + if (targetCompleted && !isHitTarget && findPitch && enableProbe.get()) { + int numProbe = (int) (probeRange.get() / probeInterval.get()); + boolean[][] arrayProbe = new boolean[numProbe * 2 + 1][numProbe * 2 + 1]; + + double targetDistance = Math.sqrt( + (targetBox.getCenter().x - mc.player.getX()) * (targetBox.getCenter().x - mc.player.getX()) + + (targetBox.getCenter().z - mc.player.getZ()) * (targetBox.getCenter().z - mc.player.getZ()) + ); + + double delta = Math.atan2(targetDistance, probeInterval.get()); + + double centerYaw = targetYaw; + double centerPitch = targetPitch; + + for (int i = 0; i < numProbe * 2 + 1; ++i) { + for (int j = 0; j < numProbe * 2 + 1; ++j) { + targetYaw = centerYaw - (i - numProbe) * delta; + targetPitch = centerPitch - (j - numProbe) * delta; + calculatePath(); + arrayProbe[i][j] = isHitTarget; } - if (!Double.isNaN(targetHighPitch)) targetHighPitch = (minPitch + maxPitch) / 2.0; } - if (!isHitTarget && !Double.isNaN(targetHighPitch) && allowHighThrows.get()) { + int[][][] validProbe = new int[numProbe * 2 + 1][numProbe * 2 + 1][4]; + + for (int i = 0; i < numProbe * 2 + 1; ++i) { + for (int j = 0; j < numProbe * 2 + 1; ++j) { + if (i == 0) validProbe[i][j][0] = arrayProbe[i][j] ? 1 : 0; + else validProbe[i][j][0] = (arrayProbe[i][j] ? 1 : 0) + validProbe[i - 1][j][0]; + if (j == 0) validProbe[i][j][1] = arrayProbe[i][j] ? 1 : 0; + else validProbe[i][j][1] = (arrayProbe[i][j] ? 1 : 0) + validProbe[i][j - 1][1]; + } + } + + for (int i = numProbe * 2; i >= 0; --i) { + for (int j = numProbe * 2; j >= 0; --j) { + if (i == numProbe * 2) validProbe[i][j][2] = arrayProbe[i][j] ? 1 : 0; + else validProbe[i][j][2] = (arrayProbe[i][j] ? 1 : 0) + validProbe[i + 1][j][2]; + if (j == numProbe * 2) validProbe[i][j][3] = arrayProbe[i][j] ? 1 : 0; + else validProbe[i][j][3] = (arrayProbe[i][j] ? 1 : 0) + validProbe[i][j + 1][3]; + } + } + + int maxProbe = 0; + targetYaw = centerYaw; + targetPitch = centerPitch; + + for (int i = numProbe * 2; i >= 0; --i) { + for (int j = numProbe * 2; j >= 0; --j) { + int x = Math.max(Math.min(validProbe[i][j][0], validProbe[i][j][1]), Math.min(validProbe[i][j][2], validProbe[i][j][3])); + if (x > maxProbe) { + maxProbe = x; + targetYaw = centerYaw - (i - numProbe) * delta; + targetPitch = centerPitch - (j - numProbe) * delta; + } + } + } + + calculatePath(); + } + + // Solve for the high pitch if allowed + if (!isHitTarget && allowHighThrows.get()) { + double targetHighPitch = highestPitch; + double minPitch = -90.0; + double maxPitch = highestPitch; + for (int i = 0; iterationSteps.get() > 0 ? i < iterationSteps.get() : maxPitch - minPitch > iterationEpsilon.get(); i++) { + double mid = (minPitch + maxPitch) / 2.0; + double midHeight = calculateHeightOffset(mid); + if (Double.isNaN(midHeight)) { + targetHighPitch = Double.NaN; + break; + } + if (midHeight <= 0) + minPitch = mid; + else maxPitch = mid; + } + + if (!Double.isNaN(targetHighPitch)) { + targetHighPitch = (minPitch + maxPitch) / 2.0; + targetPitch = targetHighPitch; targetYaw = Rotations.getYaw(targetEntity); targetCharge = getCurrentCharge(); + targetCompleted = true; calculatePath(); } - - return; } } private void aim(double tickDelta) { if (targetEntity == null) return; + if (!targetCompleted) return; + if (!isHitTarget && aimOnlyHit.get()) return; + if (!mc.options.useKey.isPressed()) return; + if (!InvUtils.testInHands(Items.BOW)) return; if (instant.get()) { mc.player.setYaw((float) targetYaw);