diff --git a/src/AcDream.Core/Physics/PhysicsEngine.cs b/src/AcDream.Core/Physics/PhysicsEngine.cs index da6711e..5868fe0 100644 --- a/src/AcDream.Core/Physics/PhysicsEngine.cs +++ b/src/AcDream.Core/Physics/PhysicsEngine.cs @@ -625,6 +625,33 @@ public sealed class PhysicsEngine bool collisionNormalValid = ci.CollisionNormalValid; Vector3 collisionNormal = ci.CollisionNormal; + // #42 diagnostic (2026-05-05): trace airborne sweeps to identify the + // source of the ~1m XY drift on retail-observed stationary jumps. + // Gated on ACDREAM_AIRBORNE_DIAG=1 and !isOnGround. One line per + // resolve call. deltaXY = post - target tells us how much the sweep + // diverged from the requested target; for a clean stationary +Z + // jump we expect (0,0). cp=valid with a tilted normal would confirm + // H1 (initial-overlap depenetration → next-step AdjustOffset projects + // the +Z offset along a non-+Z normal). User repros at flat plaza / + // east hillside / north hillside; if drift direction tracks terrain + // orientation, H1 is the cause; if it tracks actor facing, H2 / H3. + if (!isOnGround + && Environment.GetEnvironmentVariable("ACDREAM_AIRBORNE_DIAG") == "1") + { + var post = sp.CheckPos; + float dx = post.X - targetPos.X; + float dy = post.Y - targetPos.Y; + string cpInfo = ci.ContactPlaneValid + ? $"valid cpN=({ci.ContactPlane.Normal.X:F3},{ci.ContactPlane.Normal.Y:F3},{ci.ContactPlane.Normal.Z:F3})" + : "none"; + Console.WriteLine( + $"[SWEEP] airborne pre=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) " + + $"target=({targetPos.X:F3},{targetPos.Y:F3},{targetPos.Z:F3}) " + + $"post=({post.X:F3},{post.Y:F3},{post.Z:F3}) " + + $"cell={cellId:X8}->{sp.CheckCellId:X8} ok={ok} " + + $"deltaXY=({dx:F3},{dy:F3}) cp={cpInfo}"); + } + if (ok) { bool onGround = ci.ContactPlaneValid