fix(p2): cellar-lip wedge — check_other_cells must use the LIVE sphere position

Root cause of the "blocked at the last cellar step" wedge (the primary,
ramp-climb family — 20/29 captured records). The prior session's pinned
"find_walkable is never called during the step-down" was a probe artifact:
a fresh [fc-dispatch]/[step-sphere-down] trace proves Path-3 StepSphereDown
IS reached for both the carried cell and the iterated other-cell.

The real divergence is in Transition.CheckOtherCells. Retail's
check_other_cells (acclient_2013_pseudo_c.txt:272735 → (*cell+0x88)(this))
re-collides the OTHER cells against the LIVE sphere_path.global_sphere — the
position AFTER the primary insert_into_cell ran. The primary collide can MOVE
the sphere: a Path-5 full-hit dispatches step_sphere_up, and a successful
step-up climbs the foot onto the cottage floor yet still returns OK. acdream
instead reused a footCenter snapshot captured BEFORE the primary collide, so
once the lip-riser step-up climbed the foot onto the floor, check_other_cells
still queried 0171 at the pre-climb (sunk ~0.25 m below the floor) position →
the foot spuriously near-missed the very floor it had climbed onto →
neg_step_up → a doomed second step_up vs the floor normal (0,0,1) whose
step_up_slide unwound the climb → validate_transition reverted → 0% advance.

Fix: re-read footCenter = sp.GlobalSphere[0].Origin at the top of
RunCheckOtherCellsAndAdvance (one line). Pre-fix 0/29 wedge records advanced;
post-fix 20/29 climb onto Z≈94.

No regression: full Core suite 1321 pass / 4 fail (the documented baseline:
Apparatus_Grounded_50cmOffCenter, 2× DoorBugTrajectoryReplay LiveCompare_*,
BSPStepUpTests.D4) / 1 skip. The 2 door LiveCompare divergences are
byte-identical with/without the fix (the door's step_up FAILS → sphere
restored → position unchanged → footCenter == live).

Tests: CellarLipWedgeTests.Fix_StaleFootCenter_RampRecordClimbsCottageFloor +
Fix_StaleFootCenter_MajorityOfWedgeRecordsAdvance (new, GREEN).
DocumentsResidualWedge_LiveFloorCp_SlidingNormalKillsPlusY documents the
remaining 9/29 (0,-1,0)-sliding-normal +Y-kill family (slide territory,
deferred to the visual gate).

Apparatus retained (gated on ACDREAM_PROBE_INDOOR_BSP): [fc-dispatch] in
BSPQuery.FindCollisions + [step-sphere-down] in BSPQuery.StepSphereDown +
CellarLipWedgeTests.Diagnostic_TraceRecordByIndex — strip once the residual
is resolved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-05 09:15:19 +02:00
parent bc1be26907
commit cc4590f9e5
4 changed files with 208 additions and 18 deletions

View file

@ -1236,9 +1236,20 @@ public static class BSPQuery
ResolvedPolygon? polyHit = null;
ushort _polyId = 0; // step-down doesn't need the id, but the signature requires it
// TEMP diagnostic (cellar-lip wedge dispatch trace, 2026-06-05): Path 3
// reached — log the step-down probe inputs + the walkable-finder result so
// we can see whether the cottage floor is tested + accepted. STRIP after fix.
if (PhysicsDiagnostics.ProbeIndoorBspEnabled)
Console.WriteLine(System.FormattableString.Invariant(
$"[step-sphere-down] ENTER cell=0x{path.CheckCellId:X8} stepDownAmt={path.StepDownAmt:F3} walkInterp={path.WalkInterp:F3} move=({movement.X:F3},{movement.Y:F3},{movement.Z:F3}) center=({checkPos.Center.X:F3},{checkPos.Center.Y:F3},{checkPos.Center.Z:F3}) r={checkPos.Radius:F3}"));
FindWalkableInternal(root, resolved, path, validPos, movement, up,
ref polyHit, ref _polyId, ref changed);
if (PhysicsDiagnostics.ProbeIndoorBspEnabled)
Console.WriteLine(System.FormattableString.Invariant(
$"[step-sphere-down] RESULT cell=0x{path.CheckCellId:X8} changed={changed} poly={(polyHit is null ? "n/a" : $"0x{polyHit.Id:X4} n=({polyHit.Plane.Normal.X:F3},{polyHit.Plane.Normal.Y:F3},{polyHit.Plane.Normal.Z:F3})")}"));
if (changed && polyHit is not null)
{
// ACE: path.LocalSpacePos.LocalToGlobalVec(adjusted) * scale
@ -1707,6 +1718,23 @@ public static class BSPQuery
returnState: -1);
}
// TEMP diagnostic (cellar-lip wedge dispatch trace, 2026-06-05): which of
// the 6 paths does this cell take? The path is flag-driven (BSP-independent),
// so the synthetic-leaf test reproduces it faithfully. Deduce the path from
// the dispatch order so a single line names path + every gating flag.
// Gated on ACDREAM_PROBE_INDOOR_BSP. STRIP once the wedge fix lands.
if (PhysicsDiagnostics.ProbeIndoorBspEnabled)
{
int _p = (path.InsertType == InsertType.Placement || obj.Ethereal) ? 1
: path.CheckWalkable ? 2
: path.StepDown ? 3
: path.Collide ? 4
: obj.State.HasFlag(ObjectInfoState.Contact) ? 5
: 6;
Console.WriteLine(System.FormattableString.Invariant(
$"[fc-dispatch] cell=0x{path.CheckCellId:X8} PATH={_p} stepUp={path.StepUp} stepDown={path.StepDown} chkWalk={path.CheckWalkable} insert={path.InsertType} collide={path.Collide} contact={obj.State.HasFlag(ObjectInfoState.Contact)} ethereal={obj.Ethereal} c0=({sphere0.Center.X:F3},{sphere0.Center.Y:F3},{sphere0.Center.Z:F3}) hasS1={sphere1 is not null}"));
}
// Helper: transform a local-space vector to world space.
// ACE: path.LocalSpacePos.LocalToGlobalVec(v)
Vector3 L2W(Vector3 v) => Vector3.Transform(v, localToWorld);