Implement bow projectile prediction
This commit is contained in:
parent
e49f1a987d
commit
d719e0e46d
@ -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());
|
||||||
|
229
src/main/java/com/example/addon/modules/Prediction.java
Normal file
229
src/main/java/com/example/addon/modules/Prediction.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user