Implement bow projectile prediction

This commit is contained in:
Redstone1024 2024-08-26 21:06:24 +08:00
parent e49f1a987d
commit d719e0e46d
2 changed files with 232 additions and 0 deletions

View File

@ -2,7 +2,9 @@ package com.example.addon;
import com.example.addon.commands.CommandExample; import com.example.addon.commands.CommandExample;
import com.example.addon.hud.HudExample; import com.example.addon.hud.HudExample;
import com.example.addon.modules.AimAssist;
import com.example.addon.modules.ModuleExample; import com.example.addon.modules.ModuleExample;
import com.example.addon.modules.Prediction;
import com.mojang.logging.LogUtils; import com.mojang.logging.LogUtils;
import meteordevelopment.meteorclient.addons.GithubRepo; import meteordevelopment.meteorclient.addons.GithubRepo;
import meteordevelopment.meteorclient.addons.MeteorAddon; import meteordevelopment.meteorclient.addons.MeteorAddon;
@ -24,6 +26,7 @@ public class AddonTemplate extends MeteorAddon {
// Modules // Modules
Modules.get().add(new ModuleExample()); Modules.get().add(new ModuleExample());
Modules.get().add(new Prediction());
// Commands // Commands
Commands.add(new CommandExample()); Commands.add(new CommandExample());

View File

@ -0,0 +1,229 @@
package com.example.addon.modules;
import com.example.addon.AddonTemplate;
import meteordevelopment.meteorclient.events.render.Render3DEvent;
import meteordevelopment.meteorclient.renderer.ShapeMode;
import meteordevelopment.meteorclient.settings.*;
import meteordevelopment.meteorclient.systems.modules.Module;
import meteordevelopment.meteorclient.utils.Utils;
import meteordevelopment.meteorclient.utils.misc.MissHitResult;
import meteordevelopment.meteorclient.utils.misc.Pool;
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.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.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;
import org.joml.Vector3d;
import java.util.ArrayList;
import java.util.List;
public class Prediction extends Module {
private final SettingGroup sgGeneral = settings.getDefaultGroup();
private final SettingGroup sgRender = settings.createGroup("Render");
public final Setting<Integer> simulationSteps = sgGeneral.add(new IntSetting.Builder()
.name("simulation-steps")
.description("How many steps to simulate projectiles. Zero for no limit")
.defaultValue(500)
.sliderMax(5000)
.build()
);
private final Setting<SettingColor> sideColor = sgRender.add(new ColorSetting.Builder()
.name("side-color")
.description("The side color.")
.defaultValue(new SettingColor(255, 150, 0, 35))
.build()
);
private final Setting<SettingColor> lineColor = sgRender.add(new ColorSetting.Builder()
.name("line-color")
.description("The line color.")
.defaultValue(new SettingColor(255, 150, 0))
.build()
);
public Prediction() { super(AddonTemplate.CATEGORY, "bow-prediction", "Predicting arrow trajectories."); }
private final Pool<Vector3d> vectorPool = new Pool<>(Vector3d::new);
private final List<Vector3d> 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 void clearPath() {
for (Vector3d point : points) vectorPool.free(point);
points.clear();
hitQuad = false;
collidingEntity = null;
}
private void calculatePath(double tickDelta) {
clearPath();
ItemStack itemStack = mc.player.getMainHandStack();
if (!(itemStack.getItem() instanceof BowItem)) {
itemStack = mc.player.getOffHandStack();
if (!(itemStack.getItem() instanceof BowItem)) return;
}
Item item = itemStack.getItem();
double charge = BowItem.getPullProgress(mc.player.getItemUseTime());
if (charge <= 0) return;
double speed = charge * 3;
double gravity = 0.05000000074505806;
double airDrag = 0.99;
double waterDrag = 0.6;
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);
Utils.set(position, mc.player, tickDelta).add(0, mc.player.getEyeHeight(mc.player.getPose()), 0);
double yaw = MathHelper.lerp(tickDelta, mc.player.prevYaw, mc.player.getYaw());
double pitch = MathHelper.lerp(tickDelta, mc.player.prevPitch, mc.player.getPitch());
double x = -Math.sin(yaw * 0.017453292) * Math.cos(pitch * 0.017453292);
double y = -Math.sin(pitch * 0.017453292);
double z = Math.cos(yaw * 0.017453292) * Math.cos(pitch * 0.017453292);
velocity.set(x, y, z).normalize().mul(speed);
HitResult hitResult = null;
for (int i = 0; i < (simulationSteps.get() > 0 ? simulationSteps.get() : Integer.MAX_VALUE) && hitResult == null; i++) {
points.add(vectorPool.get().set(position));
lastPosition.set(position);
position.add(velocity);
boolean isTouchingWater = false;
FluidState fluidState = mc.world.getFluidState(new BlockPos((int) position.x, (int) position.y, (int) position.z));
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);
if (position.y < mc.world.getBottomY()) {
hitResult = MissHitResult.INSTANCE;
break;
}
int chunkX = ChunkSectionPos.getSectionCoord(position.x);
int chunkZ = ChunkSectionPos.getSectionCoord(position.z);
if (!mc.world.getChunkManager().isChunkLoaded(chunkX, chunkZ)) {
hitResult = MissHitResult.INSTANCE;
break;
}
hitResult = mc.world.raycast(new RaycastContext(
new Vec3d(lastPosition.x, lastPosition.y, lastPosition.z), new Vec3d(position.x, position.y, position.z),
RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, mc.player)
);
if (hitResult.getType() != HitResult.Type.MISS) {
lastPosition = new Vector3d(hitResult.getPos().x, hitResult.getPos().y, hitResult.getPos().z);
}
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;
}
hitResult = hitResult.getType() == HitResult.Type.MISS ? null : hitResult;
}
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()));
}
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));
}
}
}
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()); }
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 (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();
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);
}
}
@EventHandler
private void onRender(Render3DEvent event) {
float tickDelta = mc.world.getTickManager().isFrozen() ? 1 : event.tickDelta;
calculatePath(tickDelta);
renderPath(event);
}
}