diff --git a/tests/AcDream.Core.Tests/Physics/PhysicsBodyCellSyncTests.cs b/tests/AcDream.Core.Tests/Physics/PhysicsBodyCellSyncTests.cs index 3c1e22ed..fed0ca92 100644 --- a/tests/AcDream.Core.Tests/Physics/PhysicsBodyCellSyncTests.cs +++ b/tests/AcDream.Core.Tests/Physics/PhysicsBodyCellSyncTests.cs @@ -65,4 +65,38 @@ public class PhysicsBodyCellSyncTests body.Position += new Vector3(10f, 10f, 0f); Assert.Equal(0u, body.CellPosition.ObjCellId); } + + [Fact] + public void ContinuousTracking_LongWalkAcrossCellsAndBlock_NeverGoesStale() + { + // Refutes the "CellPosition goes stale" concern (incl. the indoor-transit case): + // because the Position setter re-derives the cell via AdjustToOutside on EVERY + // write, CellPosition continuously tracks the outdoor cell under the body's WORLD + // position. The body is always world-space, so the world delta is frame-invariant — + // there is no separate "interior" frame to desync. On any exit it is the correct + // cell, never stale. + var body = new PhysicsBody(); + body.SnapToCell(0xC95B0001u, new Vector3(1000f, 2000f, 12f), new Vector3(10f, 10f, 12f)); + + // A winding walk: east within the block, then north across the 192 m boundary. + foreach (var step in new[] + { + new Vector3(50f, 0f, 0f), + new Vector3(50f, 0f, 0f), + new Vector3(0f, 100f, 0f), + new Vector3(0f, 100f, 0f), // local Y 110 → 210, crosses north into 0xC95C + }) + body.Position += step; + + // local accum (10,10)+(100,200) = (110,210); Y 210 wraps to the north neighbour + // 0xC95C (lbY 0x5B → 0x5C), local Y = 210−192 = 18; cell (lx=4, ly=0) → low 0x21. + Assert.Equal(0xC95C0021u, body.CellPosition.ObjCellId); + Assert.Equal(new Vector3(110f, 18f, 12f), body.CellPosition.Frame.Origin); + + // And the carried (cell, local) is already canonical — re-deriving does not march. + uint cell = body.CellPosition.ObjCellId; + var local = body.CellPosition.Frame.Origin; + Assert.True(LandDefs.AdjustToOutside(ref cell, ref local)); + Assert.Equal(body.CellPosition.ObjCellId, cell); + } }