Add Metalstorm-style cursor aim

Virtual stick cursor steered through the gun reticle, server-side virtual pilot
using measured control responses, client cursor/camera handling with look-ahead,
free look and scroll zoom.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVREx3uo7nz1FEhzveWTUh
This commit is contained in:
Claude committed 2026-11-11 18:29:13 +01:00
1 parent d376a6b9fa
commit 8bb2cfd859
17 files changed
+1037 -38

No files matched your search

@@ -0,0 +1,101 @@
package dev.lumareth.improvedAerodynamics.flight;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.Property;
/**
* The frame cursor aim steers in, in ship coordinates: forward is the boresight (what Warium's gun reticle points
* along, from the HUD block in front of the seat, else the way the seat faces), up is the ship's up axis and right
* completes it. Server and client both build it from the same block data, so a cursor sent as components in this
* frame means the same on both.
*
* @param nodePos the Vehicle Control Node the seat is linked to
*/
public record AimBasis(BlockPos seatPos, BlockPos nodePos, double[] forward, double[] right, double[] up) {
/**
* @param entityPos block position of the seat entity. Warium's control seat block is at that position or just
* below it.
* @return null if there is no seat block linked to a control node there
*/
public static AimBasis of(Level level, BlockPos entityPos) {
for (BlockPos pos : new BlockPos[]{entityPos, entityPos.below()}) {
BlockEntity seat = level.getBlockEntity(pos);
if (seat == null || !seat.getPersistentData().contains("ControlX")) {
continue;
}
CompoundTag tag = seat.getPersistentData();
BlockPos nodePos = BlockPos.containing(tag.getDouble("ControlX"), tag.getDouble("ControlY"), tag.getDouble("ControlZ"));
return new AimBasis(pos, nodePos, null, null, null).withAxes(level, pos, nodePos);
}
return null;
}
private AimBasis withAxes(Level level, BlockPos seatPos, BlockPos nodePos) {
BlockState state = level.getBlockState(seatPos);
Direction facing = Direction.NORTH;
Property<?> property = state.getBlock().getStateDefinition().getProperty("facing");
if (property instanceof DirectionProperty directionProperty) {
facing = state.getValue(directionProperty);
}
double fx = facing.getStepX();
double fy = facing.getStepY();
double fz = facing.getStepZ();
// Warium's gun reticle: the HUD block in front of the seat holds the aim vector of its guns
BlockEntity hud = level.getBlockEntity(seatPos.relative(facing));
if (hud != null && hud.getPersistentData().contains("Pitch")) {
CompoundTag hudTag = hud.getPersistentData();
double bx = hudTag.getDouble("X") + facing.getStepX();
double by = hudTag.getDouble("Pitch");
double bz = hudTag.getDouble("Z") + facing.getStepZ();
if (bx * bx + by * by + bz * bz > 1.0E-6) {
fx = bx;
fy = by;
fz = bz;
}
}
double length = Math.sqrt(fx * fx + fy * fy + fz * fz);
if (length < 1.0E-6) {
fx = 0.0;
fy = 0.0;
fz = -1.0;
length = 1.0;
}
fx /= length;
fy /= length;
fz /= length;
// right = forward x up(0,1,0)
double rx = -fz;
double ry = 0.0;
double rz = fx;
double rl = Math.sqrt(rx * rx + rz * rz);
if (rl < 1.0E-6) {
rx = 1.0;
rz = 0.0;
rl = 1.0;
}
rx /= rl;
rz /= rl;
// up = right x forward
double ux = ry * fz - rz * fy;
double uy = rz * fx - rx * fz;
double uz = rx * fy - ry * fx;
return new AimBasis(seatPos, nodePos, new double[]{fx, fy, fz}, new double[]{rx, ry, rz}, new double[]{ux, uy, uz});
}
/** Ship-space direction of a cursor given by its components along forward, right and up. */
public double[] toShip(double cf, double cr, double cu) {
return new double[]{
forward[0] * cf + right[0] * cr + up[0] * cu,
forward[1] * cf + right[1] * cr + up[1] * cu,
forward[2] * cf + right[2] * cr + up[2] * cu};
}
}