From 5b4a9fb908282b7281e3af2f0278b933c1eab56e Mon Sep 17 00:00:00 2001 From: Redstone1024 <2824517378@qq.com> Date: Tue, 27 Aug 2024 16:10:26 +0800 Subject: [PATCH] Add parabolic prediction and auto-aiming --- .../com/example/addon/modules/Prediction.java | 293 +++++++++++++++++- 1 file changed, 276 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/example/addon/modules/Prediction.java b/src/main/java/com/example/addon/modules/Prediction.java index 686f799..53e9005 100644 --- a/src/main/java/com/example/addon/modules/Prediction.java +++ b/src/main/java/com/example/addon/modules/Prediction.java @@ -2,22 +2,34 @@ package com.example.addon.modules; import com.example.addon.AddonTemplate; import meteordevelopment.meteorclient.events.render.Render3DEvent; +import meteordevelopment.meteorclient.events.world.TickEvent; import meteordevelopment.meteorclient.renderer.ShapeMode; import meteordevelopment.meteorclient.settings.*; +import meteordevelopment.meteorclient.systems.friends.Friends; import meteordevelopment.meteorclient.systems.modules.Module; import meteordevelopment.meteorclient.utils.Utils; +import meteordevelopment.meteorclient.utils.entity.EntityUtils; +import meteordevelopment.meteorclient.utils.entity.SortPriority; +import meteordevelopment.meteorclient.utils.entity.TargetUtils; import meteordevelopment.meteorclient.utils.misc.MissHitResult; import meteordevelopment.meteorclient.utils.misc.Pool; +import meteordevelopment.meteorclient.utils.player.InvUtils; +import meteordevelopment.meteorclient.utils.player.PlayerUtils; +import meteordevelopment.meteorclient.utils.player.Rotations; import meteordevelopment.meteorclient.utils.render.color.SettingColor; import meteordevelopment.orbit.EventHandler; import net.minecraft.entity.Entity; 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.Item; 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; @@ -27,12 +39,76 @@ import org.joml.Vector3d; import java.util.ArrayList; import java.util.List; +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 sgSpeed = settings.createGroup("Aim Speed"); private final SettingGroup sgRender = settings.createGroup("Render"); - public final Setting simulationSteps = sgGeneral.add(new IntSetting.Builder() + private final Setting predictionLevel = sgGeneral.add(new DoubleSetting.Builder() + .name("prediction-level") + .description("The intelligence level for entity position prediction.") + .defaultValue(0) + .range(0, 0) + .sliderMax(0) + .build() + ); + + private final Setting aimAssist = sgGeneral.add(new BoolSetting.Builder() + .name("aim-assist") + .description("Automatically aims at entities.") + .defaultValue(true) + .build() + ); + + private final Setting range = sgTarget.add(new DoubleSetting.Builder() + .name("range") + .description("The maximum range the entity can be to aim at it.") + .defaultValue(32) + .range(0, 128) + .sliderMax(128) + .build() + ); + + private final Setting>> entities = sgTarget.add(new EntityTypeListSetting.Builder() + .name("entities") + .description("Entities to attack.") + .defaultValue(EntityType.PLAYER) + .onlyAttackable() + .build() + ); + + private final Setting priority = sgTarget.add(new EnumSetting.Builder() + .name("priority") + .description("What type of entities to targetEntity.") + .defaultValue(SortPriority.LowestHealth) + .build() + ); + + private final Setting babies = sgTarget.add(new BoolSetting.Builder() + .name("babies") + .description("Whether or not to attack baby variants of the entity.") + .defaultValue(true) + .build() + ); + + private final Setting nametagged = sgTarget.add(new BoolSetting.Builder() + .name("nametagged") + .description("Whether or not to attack mobs with a name tag.") + .defaultValue(false) + .build() + ); + + private final Setting ignoreWalls = sgTarget.add(new BoolSetting.Builder() + .name("ignore-walls") + .description("Whether or not to ignore aiming through walls.") + .defaultValue(false) + .build() + ); + + public final Setting simulationSteps = sgTarget.add(new IntSetting.Builder() .name("simulation-steps") .description("How many steps to simulate projectiles. Zero for no limit") .defaultValue(500) @@ -40,22 +116,121 @@ public class Prediction extends Module { .build() ); - private final Setting sideColor = sgRender.add(new ColorSetting.Builder() - .name("side-color") - .description("The side color.") - .defaultValue(new SettingColor(255, 150, 0, 35)) + private final Setting instant = sgSpeed.add(new BoolSetting.Builder() + .name("instant-look") + .description("Instantly looks at the entity.") + .defaultValue(false) .build() ); - private final Setting lineColor = sgRender.add(new ColorSetting.Builder() - .name("line-color") - .description("The line color.") + private final Setting speed = sgSpeed.add(new DoubleSetting.Builder() + .name("speed") + .description("How fast to aim at the entity.") + .defaultValue(5) + .min(0) + .visible(() -> !instant.get()) + .build() + ); + + private final Setting enableRender = sgRender.add(new BoolSetting.Builder() + .name("enable-render") + .description("Enable/Disable Render Indication.") + .defaultValue(true) + .build() + ); + + private final Setting shapeMode = sgRender.add(new EnumSetting.Builder() + .name("shape-mode") + .description("How the shapes are rendered.") + .defaultValue(ShapeMode.Both) + .visible(enableRender::get) + .build() + ); + + private final Setting missSideColor = sgRender.add(new ColorSetting.Builder() + .name("miss-side-color") + .description("The side color for miss.") + .defaultValue(new SettingColor(255, 150, 0, 35)) + .visible(enableRender::get) + .build() + ); + + private final Setting missLineColor = sgRender.add(new ColorSetting.Builder() + .name("miss-line-color") + .description("The line color for miss.") .defaultValue(new SettingColor(255, 150, 0)) + .visible(enableRender::get) + .build() + ); + + private final Setting hitSideColor = sgRender.add(new ColorSetting.Builder() + .name("hit-side-color") + .description("The side color for hit.") + .defaultValue(new SettingColor(128, 255, 0, 35)) + .visible(enableRender::get) + .build() + ); + + private final Setting hitLineColor = sgRender.add(new ColorSetting.Builder() + .name("hit-line-color") + .description("The line color for hit.") + .defaultValue(new SettingColor(128, 255, 0)) + .visible(enableRender::get) .build() ); public Prediction() { super(AddonTemplate.CATEGORY, "bow-prediction", "Predicting arrow trajectories."); } + @Override + public void onDeactivate() { + targetEntity = null; + isHitTarget = false; + } + + @EventHandler + private void onTick(TickEvent.Post event) { } + + @EventHandler + private void onRender(Render3DEvent event) { + float tickDelta = mc.world.getTickManager().isFrozen() ? 1 : event.tickDelta; + + if (mc.options.attackKey.isPressed() || !isSelectableTarget(targetEntity)) { + targetEntity = TargetUtils.get(this::isSelectableTarget, priority.get()); + isHitTarget = false; + } + + calculatePath(tickDelta); + if (aimAssist.get()) calculateAngle(tickDelta); + if (aimAssist.get() && mc.options.useKey.isPressed() && InvUtils.testInHands(Items.BOW)) aim(event.tickDelta); + if (enableRender.get()) renderPath(event); + } + + @Override + public String getInfoString() { + return EntityUtils.getName(targetEntity); + } + + private boolean isSelectableTarget(Entity entity) { + if (entity == null) return false; + if (entity == mc.player || entity == mc.cameraEntity) return false; + if ((entity instanceof LivingEntity && ((LivingEntity) entity).isDead()) || !entity.isAlive()) return false; + if (!PlayerUtils.isWithin(entity, range.get())) return false; + if (!entities.get().contains(entity.getType())) return false; + if (!nametagged.get() && entity.hasCustomName()) return false; + if (!ignoreWalls.get() && !PlayerUtils.canSeeEntity(entity)) return false; + if (entity instanceof PlayerEntity) { + if (((PlayerEntity) entity).isCreative()) return false; + if (!Friends.get().shouldAttack((PlayerEntity) entity)) return false; + } + return !(entity instanceof AnimalEntity) || babies.get() || !((AnimalEntity) entity).isBaby(); + } + + private Entity targetEntity; + private boolean isHitTarget; + + private double targetYaw; + private double targetPitch; + private final Pool vectorPool = new Pool<>(Vector3d::new); private final List points = new ArrayList<>(); private boolean hitQuad = false, hitQuadHorizontal = false; @@ -73,6 +248,7 @@ public class Prediction extends Module { private void calculatePath(double tickDelta) { clearPath(); + isHitTarget = false; ItemStack itemStack = mc.player.getMainHandStack(); if (!(itemStack.getItem() instanceof BowItem)) { @@ -193,6 +369,7 @@ public class Prediction extends Module { 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; } } } @@ -200,13 +377,32 @@ public class Prediction extends Module { private void renderPath(Render3DEvent event) { Vector3d lastPoint = null; for (Vector3d point : points) { - if (lastPoint != null) { event.renderer.line(lastPoint.x, lastPoint.y, lastPoint.z, point.x, point.y, point.z, lineColor.get()); } + if (lastPoint != null) { + event.renderer.line( + lastPoint.x, lastPoint.y, lastPoint.z, point.x, point.y, point.z, + isHitTarget ? hitLineColor.get() : missLineColor.get() + ); + } lastPoint = point; } if (hitQuad) { - if (hitQuadHorizontal) event.renderer.sideHorizontal(hitQuad1.x, hitQuad1.y, hitQuad1.z, hitQuad1.x + 0.5, hitQuad1.z + 0.5, sideColor.get(), lineColor.get(), ShapeMode.Both); - else event.renderer.sideVertical(hitQuad1.x, hitQuad1.y, hitQuad1.z, hitQuad2.x, hitQuad2.y, hitQuad2.z, sideColor.get(), lineColor.get(), ShapeMode.Both); + if (hitQuadHorizontal) { + event.renderer.sideHorizontal( + hitQuad1.x, hitQuad1.y, hitQuad1.z, hitQuad1.x + 0.5, hitQuad1.z + 0.5, + isHitTarget ? hitSideColor.get() : missSideColor.get(), + isHitTarget ? hitLineColor.get() : missLineColor.get(), + shapeMode.get() + ); + } + else { + event.renderer.sideVertical( + hitQuad1.x, hitQuad1.y, hitQuad1.z, hitQuad2.x, hitQuad2.y, hitQuad2.z, + isHitTarget ? hitSideColor.get() : missSideColor.get(), + isHitTarget ? hitLineColor.get() : missLineColor.get(), + shapeMode.get() + ); + } } if (collidingEntity != null) { @@ -215,15 +411,78 @@ public class Prediction extends Module { double z = (collidingEntity.getZ() - collidingEntity.prevZ) * event.tickDelta; Box box = collidingEntity.getBoundingBox(); - event.renderer.box(x + box.minX, y + box.minY, z + box.minZ, x + box.maxX, y + box.maxY, z + box.maxZ, sideColor.get(), lineColor.get(), ShapeMode.Both, 0); + event.renderer.box( + x + box.minX, y + box.minY, z + box.minZ, x + box.maxX, y + box.maxY, z + box.maxZ, + 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 + ); } } - @EventHandler - private void onRender(Render3DEvent event) { - float tickDelta = mc.world.getTickManager().isFrozen() ? 1 : event.tickDelta; - calculatePath(tickDelta); - renderPath(event); + private void calculateAngle(double tickDelta) { + if (targetEntity == null) return; + + float velocity = (mc.player.getItemUseTime() - mc.player.getItemUseTimeLeft()) / 20f; + velocity = (velocity * velocity + velocity * 2) / 3; + if (velocity > 1) velocity = 1; + + double posX = targetEntity.getPos().getX() + (targetEntity.getPos().getX() - targetEntity.prevX) * tickDelta; + double posY = targetEntity.getPos().getY() + (targetEntity.getPos().getY() - targetEntity.prevY) * tickDelta; + double posZ = targetEntity.getPos().getZ() + (targetEntity.getPos().getZ() - targetEntity.prevZ) * tickDelta; + + posY -= 1.9f - targetEntity.getHeight(); + + double relativeX = posX - mc.player.getX(); + double relativeY = posY - mc.player.getY(); + double relativeZ = posZ - mc.player.getZ(); + + double hDistance = Math.sqrt(relativeX * relativeX + relativeZ * relativeZ); + double hDistanceSq = hDistance * hDistance; + float g = 0.006f; + float velocitySq = velocity * velocity; + float pitch = (float) -Math.toDegrees(Math.atan((velocitySq - Math.sqrt(velocitySq * velocitySq - g * (g * hDistanceSq + 2 * relativeY * velocitySq))) / (g * hDistance))); + + if (Float.isNaN(pitch)) { + targetYaw = Rotations.getYaw(targetEntity); + targetPitch = Rotations.getPitch(targetEntity); + } else { + targetYaw = Rotations.getYaw(new Vec3d(posX, posY, posZ)); + targetPitch = pitch; + } } + private void aim(double tickDelta) { + if (targetEntity == null) return; + + if (instant.get()) { + mc.player.setYaw((float) targetYaw); + } else { + double deltaAngle = MathHelper.wrapDegrees(targetYaw - mc.player.getYaw()); + double toRotate = speed.get() * (deltaAngle >= 0 ? 1 : -1) * tickDelta; + if ((toRotate >= 0 && toRotate > deltaAngle) || (toRotate < 0 && toRotate < deltaAngle)) toRotate = deltaAngle; + mc.player.setYaw(mc.player.getYaw() + (float) toRotate); + } + + if (instant.get()) { + mc.player.setPitch((float) targetPitch); + } else { + double deltaAngle = MathHelper.wrapDegrees(targetPitch - mc.player.getPitch()); + double toRotate = speed.get() * (deltaAngle >= 0 ? 1 : -1) * tickDelta; + if ((toRotate >= 0 && toRotate > deltaAngle) || (toRotate < 0 && toRotate < deltaAngle)) toRotate = deltaAngle; + mc.player.setPitch(mc.player.getPitch() + (float) toRotate); + } + } }