fix(phys): A6.P4 door bug — AddAllOutsideCells coord convention + replay apparatus
CellTransit.AddAllOutsideCells assumed sphere coords were absolute world coords (subtracting lbXf = 0xA9 * 192 = 32448 from the sphere position). Production has used landblock-local coords since Phase A.1 (streaming-center landblock at world origin), so the subtraction produced localX = -32316, gridX = -1346 → out-of-range → early return → ZERO outdoor cells added. For outdoor primary cells the bug was masked by GetNearbyObjects's radial sweep. For indoor primary cells (where #98 gates the outdoor sweep), the door's outdoor cell 0xA9B40029 never reached portalReachableCells, the door's BSP was never queried, and the player walked through Holtburg cottage doors unimpeded. Fix: AddAllOutsideCells treats worldSphereCenter as landblock-local directly. Matches retail CLandCell::add_all_outside_cells which uses the per-cell 6-byte landblock-relative position struct. Existing CellTransitAddAllOutsideCellsTests + CellTransitFindCellSetTests updated to use landblock-local sphere coords (they were the only callers using the world-coord convention; production never did). Apparatus shipped: - DoorBugTrajectoryReplayTests — live-capture-driven replay harness that pinpointed the bug per-field at unit-test speed (<500ms iteration) - AddAllOutsideCells_LandblockLocalSphere_AddsDoorOutdoorCell — direct unit test that demonstrates the fix - FindTransitCellsSphere_IndoorExitPortal_AddsOutsideForCapturedSpherePos — verifies cell-portal traversal at the captured sphere position - DoorSetupGfxObjInspectionTests.HoltburgCottage_CellPortals_DatInspection — dat-direct EnvCell + Environment.Cells + portal-poly inspector - Fixture: tests/AcDream.Core.Tests/Fixtures/door-bug/live-capture.jsonl (tick 13558 walkthrough + tick 22760 outdoor block) Visual verification (user-driven at Holtburg cottage door, ~50cm off-center): - outside→inside RUN: now BLOCKS (was: walks through) - outside→inside WALK: presumed blocks (not retested) - inside→outside RUN: PARTIAL — body intersects door, sphere slides through - inside→outside WALK: same partial behavior The remaining inside→outside asymmetry is a SEPARATE bug in BSP collision response for two-sided polygons. The [bsp-test] probe now fires 245 times for the door entity from indoor (was 0 pre-fix) — door IS being queried; the BSP polygon-level collision response is the new bug. Handoff at docs/research/2026-05-25-door-bug-partial-fix-shipped.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6a2c432e5a
commit
28cd97be62
8 changed files with 1134 additions and 40 deletions
|
|
@ -110,21 +110,11 @@ public class CellTransitFindCellSetTests
|
|||
public void FindCellSet_OutdoorSeed_IncludesNeighbourLandcells()
|
||||
{
|
||||
var cache = new PhysicsDataCache();
|
||||
// Outdoor seed near a cell boundary — expand to neighbours via
|
||||
// AddAllOutsideCells. Landcells have no CellPhysics in cache, so
|
||||
// they appear in the set but the containing-cell loop falls back
|
||||
// to currentCellId. The point of this test: the SET captures
|
||||
// them even though FindCellList's single-uint return cannot.
|
||||
//
|
||||
// World coords for landblock 0xA9B4FFFF: origin at
|
||||
// (0xA9*192, 0xB4*192) = (32448, 34560). Cell grid(0,0) covers
|
||||
// world XY in [(32448,34560), (32472,34584)). Place the sphere
|
||||
// center near the east boundary of grid(0,0) so AddAllOutsideCells
|
||||
// A6.P4 (2026-05-24): sphere coords are LANDBLOCK-LOCAL (X/Y in
|
||||
// [0, 192]). Place the sphere center near the east boundary of
|
||||
// landcell grid(0,0) (i.e., near local X=24) so AddAllOutsideCells
|
||||
// adds the east neighbour grid(1,0).
|
||||
uint lbPrefix = 0xA9B40000u;
|
||||
float lbX = ((lbPrefix >> 24) & 0xFFu) * 192f;
|
||||
float lbY = ((lbPrefix >> 16) & 0xFFu) * 192f;
|
||||
var sphereCenter = new Vector3(lbX + 23.8f, lbY + 12f, 0f);
|
||||
var sphereCenter = new Vector3(23.8f, 12f, 0f);
|
||||
|
||||
uint containing = CellTransit.FindCellSet(
|
||||
cache, sphereCenter, sphereRadius: 0.5f,
|
||||
|
|
@ -138,17 +128,12 @@ public class CellTransitFindCellSetTests
|
|||
[Fact]
|
||||
public void IndoorSeed_ExitPortalTouchedOnlyBySecondSphere_AddsOutdoorLandcell()
|
||||
{
|
||||
// Retail CObjCell::find_cell_list passes every SPHEREPATH sphere into
|
||||
// CEnvCell::find_transit_cells. When any sphere straddles an outdoor
|
||||
// exit portal, CLandCell::add_all_outside_cells runs for the whole
|
||||
// sphere array.
|
||||
uint lbPrefix = 0xA9B40000u;
|
||||
float lbX = ((lbPrefix >> 24) & 0xFFu) * 192f;
|
||||
float lbY = ((lbPrefix >> 16) & 0xFFu) * 192f;
|
||||
|
||||
var cellTransform = Matrix4x4.CreateTranslation(new Vector3(lbX, lbY, 0f));
|
||||
// A6.P4 (2026-05-24): landblock-local sphere coords. Cell sits at
|
||||
// the landblock origin (identity transform), so cell-local == world.
|
||||
// Sphere head at local (2, 12, 3.2) reaches the cell's exit portal
|
||||
// plane at local X=2.5 → AddAllOutsideCells fires.
|
||||
var exitCell = MakeCellWithPortalAtRightWall(
|
||||
cellTransform,
|
||||
Matrix4x4.Identity,
|
||||
otherCellId: 0xFFFF,
|
||||
flags: 0);
|
||||
|
||||
|
|
@ -158,10 +143,10 @@ public class CellTransitFindCellSetTests
|
|||
var spheres = new[]
|
||||
{
|
||||
// Foot sphere is not near the exit portal plane at local x=2.5.
|
||||
new Sphere { Origin = new Vector3(lbX + 0.0f, lbY + 12.0f, 2.5f), Radius = 0.5f },
|
||||
new Sphere { Origin = new Vector3(0f, 12f, 2.5f), Radius = 0.5f },
|
||||
// Head sphere reaches the exit portal plane and should trigger
|
||||
// outdoor landcell expansion.
|
||||
new Sphere { Origin = new Vector3(lbX + 2.0f, lbY + 12.0f, 3.2f), Radius = 0.5f },
|
||||
new Sphere { Origin = new Vector3(2f, 12f, 3.2f), Radius = 0.5f },
|
||||
};
|
||||
|
||||
uint containing = CellTransit.FindCellSet(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue