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:
parent
bc1be26907
commit
cc4590f9e5
4 changed files with 208 additions and 18 deletions
|
|
@ -340,26 +340,123 @@ public class CellarLipWedgeTests
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// FAITHFUL documents-the-bug (passes while the wedge exists; FAILS when the
|
||||
/// fix lands → flip to assert the climb). Replays a captured FLOOR-contact-
|
||||
/// plane wedge through the lip-cell engine; the player is STUCK (0% advance).
|
||||
/// TEMP diagnostic (2026-06-05): trace ONE record by index with full probes,
|
||||
/// to a per-index %TEMP%/lip-trace-{idx}.log. Used to compare a ramp record
|
||||
/// (no sliding normal) against a floor record. STRIP after fix.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData(6)] // STILL 0% post-footCenter-fix: flat floor, sliding normal (0,-1,0)
|
||||
[InlineData(13)] // STILL 0% post-footCenter-fix: ramp, NO sliding normal, motion -X,+Y
|
||||
[InlineData(0)] // STILL 0% post-footCenter-fix: ramp, sliding normal (0,-1,0)
|
||||
[InlineData(21)] // STILL 0% post-footCenter-fix: ramp, NO slide, motion -X,-Y (away?)
|
||||
public void Diagnostic_TraceRecordByIndex(int idx)
|
||||
{
|
||||
var rec = LoadWedgeRecords()[idx];
|
||||
var saved = Console.Out;
|
||||
var sw = new StringWriter();
|
||||
PhysicsDiagnostics.ProbeIndoorBspEnabled = true;
|
||||
PhysicsDiagnostics.ProbeStepWalkEnabled = true;
|
||||
Environment.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", "1");
|
||||
Console.SetOut(sw);
|
||||
try
|
||||
{
|
||||
var (res, req, adv) = ReplayRecord(rec);
|
||||
Console.SetOut(saved);
|
||||
var bb = rec.BodyBefore!;
|
||||
File.WriteAllText(Path.Combine(Path.GetTempPath(), $"lip-trace-{idx}.log"),
|
||||
$"record #{idx} cur=({rec.Input.CurrentPos.X:F4},{rec.Input.CurrentPos.Y:F4},{rec.Input.CurrentPos.Z:F4}) " +
|
||||
$"tgt=({rec.Input.TargetPos.X:F4},{rec.Input.TargetPos.Y:F4},{rec.Input.TargetPos.Z:F4}) " +
|
||||
$"cp=({bb.ContactPlane.Normal.X:F2},{bb.ContactPlane.Normal.Y:F2},{bb.ContactPlane.Normal.Z:F2}) " +
|
||||
$"slide=({bb.SlidingNormal.X:F2},{bb.SlidingNormal.Y:F2},{bb.SlidingNormal.Z:F2}) ts=0x{bb.TransientState:X2} " +
|
||||
$"req={req:F3} adv={adv:F3} res=({res.X:F4},{res.Y:F4},{res.Z:F4})\n\n" + sw.ToString());
|
||||
Assert.True(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.SetOut(saved);
|
||||
Environment.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", null);
|
||||
PhysicsDiagnostics.ProbeIndoorBspEnabled = false;
|
||||
PhysicsDiagnostics.ProbeStepWalkEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FIX VALIDATION (2026-06-05) — the stale-footCenter fix in
|
||||
/// <c>RunCheckOtherCellsAndAdvance</c>. Retail's <c>check_other_cells</c>
|
||||
/// (acclient_2013_pseudo_c.txt:272735) re-collides the OTHER cells against the
|
||||
/// LIVE <c>sphere_path.global_sphere</c> — i.e. AFTER the primary cell's
|
||||
/// <c>insert_into_cell</c> may have moved the sphere via a successful
|
||||
/// <c>step_sphere_up</c>. acdream captured the foot-sphere center BEFORE the
|
||||
/// primary collide and reused that stale snapshot, so once the lip-riser
|
||||
/// step_up climbed the foot onto the cottage floor, <c>check_other_cells</c>
|
||||
/// still queried 0171 at the pre-climb (sunk, penetrating) position → the foot
|
||||
/// spuriously near-missed the very floor it had climbed onto → a doomed second
|
||||
/// step_up against the floor normal whose slide unwound the climb →
|
||||
/// validate_transition reverted → 0% advance.
|
||||
///
|
||||
/// <para>
|
||||
/// <b>Root cause (traced via <c>Diagnostic_ReplayFloorCpRecord_StepUpProbes</c>):</b>
|
||||
/// the player is at the doorway EDGE of the cottage floor (0171, poly 0x0023,
|
||||
/// Z=94). The step-up's step-down finds that floor and the 0.48 sphere
|
||||
/// OVERLAPS it (0.085 m below), but acdream's walkable check REJECTS it
|
||||
/// because the sphere center projects outside the floor poly's edge
|
||||
/// (<c>insideEdges=False</c>, <c>gap=−0.395</c>) → no contact plane → step-up
|
||||
/// fails → StepUpSlide=Collided. Retail accepts the floor at its edge and
|
||||
/// crosses (0175 never blocks). Fix is in the walkable-edge acceptance
|
||||
/// (WalkableHitsSphere / PolygonHitsSpherePrecise / CheckWalkable edge math)
|
||||
/// — compare retail CPolygon::walkable_hits_sphere + check_walkable. DOOR
|
||||
/// REGRESSION RISK: walkable changes are global; visual-gate + door tests.
|
||||
/// Pre-fix: 0/29 captured wedge records advanced. Post-fix: the ramp-climb
|
||||
/// family (≈20/29) advances onto the cottage floor (Z≈94). This asserts a
|
||||
/// representative ramp record (#9, cp Z=0.78, no sliding normal) now climbs.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DocumentsWedge_LiveFloorCp_PlayerStuckAtCottageFloorEdge()
|
||||
public void Fix_StaleFootCenter_RampRecordClimbsCottageFloor()
|
||||
{
|
||||
var rec = LoadWedgeRecords()[9];
|
||||
var (res, requested, advance) = ReplayRecord(rec);
|
||||
Assert.True(advance > 0.25f * requested && res.Z >= CottageFloorZ - 0.05f,
|
||||
$"Expected ramp record #9 to climb onto the cottage floor after the " +
|
||||
$"stale-footCenter fix. advance={advance:F3} (req={requested:F3}), " +
|
||||
$"res=({res.X:F3},{res.Y:F3},{res.Z:F3}); want advance>0.25·req and Z≥{CottageFloorZ - 0.05f:F2}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FIX REGRESSION GUARD (2026-06-05): the majority of captured wedge records
|
||||
/// advance after the stale-footCenter fix. Pre-fix 0/29 → post-fix ≈20/29.
|
||||
/// A drop here means <c>check_other_cells</c> is again querying other cells at
|
||||
/// a stale pre-step_up position (the cellar-lip wedge regressed).
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Fix_StaleFootCenter_MajorityOfWedgeRecordsAdvance()
|
||||
{
|
||||
var recs = LoadWedgeRecords();
|
||||
int advanced = 0, total = 0;
|
||||
foreach (var rec in recs)
|
||||
{
|
||||
if (rec.BodyBefore is null) continue;
|
||||
total++;
|
||||
var (res, req, adv) = ReplayRecord(rec);
|
||||
if (adv > 0.25f * req) advanced++;
|
||||
}
|
||||
Assert.True(advanced >= 18,
|
||||
$"Expected ≥18 of {total} captured wedge records to advance >0.25·req " +
|
||||
$"after the stale-footCenter fix; got {advanced}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DOCUMENTS-THE-BUG (passes while a RESIDUAL wedge exists; flip when fixed).
|
||||
/// Record #6 is a FLOOR-contact-plane record that ALSO carries a stale
|
||||
/// <c>(0,-1,0)</c> sliding normal (the cottage south wall). The stale-footCenter
|
||||
/// fix does NOT clear it: <c>AdjustOffset</c>'s slide-crease projects the
|
||||
/// into-cottage +Y motion onto the floor×wall crease (the world X axis) and
|
||||
/// ZEROES it before the sphere moves, so only the −X residual survives → it
|
||||
/// full-hits the slab's −X wall → a step_up that fails on the flat floor (no CP)
|
||||
/// → Collided → revert → 0% advance.
|
||||
///
|
||||
/// <para>
|
||||
/// This is the SEPARATE "(0,-1,0) sliding-normal +Y-kill" family (7/29 records).
|
||||
/// It is slide-recovery territory — explicitly OUT OF SCOPE for this pass per
|
||||
/// the kickoff ("do not re-investigate ... slide") — and is suspected to be a
|
||||
/// buggy-trajectory artifact (the stale slide accumulated only because the
|
||||
/// player was already oscillating; once the ramp-climb advances cleanly the
|
||||
/// player should not enter the south-wall-slide-into-doorway state). The visual
|
||||
/// gate decides whether it needs a follow-up. If the residual is fixed, flip
|
||||
/// this to require advance > 0.25·requested.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DocumentsResidualWedge_LiveFloorCp_SlidingNormalKillsPlusY()
|
||||
{
|
||||
var recs = LoadWedgeRecords();
|
||||
var rec = recs.First(r => r.BodyBefore is not null
|
||||
|
|
@ -367,10 +464,10 @@ public class CellarLipWedgeTests
|
|||
var (res, requested, advance) = ReplayRecord(rec);
|
||||
var c = rec.Input.CurrentPos; var t = rec.Input.TargetPos;
|
||||
Assert.True(advance < 0.1f * requested,
|
||||
$"DOCUMENTS-THE-BUG: expected the player STUCK at the cottage-floor edge. " +
|
||||
$"DOCUMENTS-RESIDUAL: expected the player STUCK (sliding-normal +Y-kill). " +
|
||||
$"Instead it advanced: cur=({c.X:F3},{c.Y:F3},{c.Z:F3}) tgt=({t.X:F3},{t.Y:F3},{t.Z:F3}) " +
|
||||
$"res=({res.X:F3},{res.Y:F3},{res.Z:F3}) requested={requested:F3} advance={advance:F3}. " +
|
||||
$"If the walkable-edge fix landed, FLIP this to require advance>0.25·requested.");
|
||||
$"If the slide +Y-kill residual is fixed, FLIP this to require advance>0.25·requested.");
|
||||
}
|
||||
|
||||
private static PhysicsEngine BuildEngineWithLipFixtures()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue