fix #137 (corridor phantom resolved): slide_sphere opposing branch returns Collided; the 'wall' was synthetic
The mechanism-1 theory (PortalSide portal polys solid in our physics set)
is REFUTED for the corridor repro, and the remaining half of the phantom
is fixed — no cdb session needed:
- The live hit normal (-1.00,0.03,-0.03) matches NO dat polygon: a
world-space sweep of both seam cells + every portal-adjacent neighbor
(CorridorSeam_FindPolygonMatchingLiveHit) returns zero candidates. The
normal is the negated movement direction — the SYNTHETIC value
slide_sphere's opposing-normals branch records (reversed = -gDelta).
- Cell 0x8A02016E has IDENTITY rotation (the prior session's 'rotation
maps the portal planes into the -X wall' was a misattribution). The
PortalSide polys to 0x011E are +-Y planes 1.4 m beside the player's
track, perpendicular to the +X run — pos_hits_sphere's directional
cull rejects them for that movement. They ARE referenced by the dat's
physics-BSP leaves (CorridorCell_PhysicsBspLeafMembership), so retail
tests them too when approached into their plane; the dat's
keep-PortalSide / strip-ExactMatch asymmetry reads as intentional
(solid window/grate-class portals). No portal-poly filter — exactly
the blanket-skip the pickup warned against.
- Port fix: CSphere::slide_sphere's opposing-normals branch
(0x005375d7-0x0053762c) records the reversed displacement and returns
COLLIDED_TS; our port returned OK ('retail returns OK here' was a
decomp misread), letting the step complete as-is with the synthetic
collision normal that validate's epilogue then persisted as the
sliding normal the wedge absorbed on. TransitionTypes opposing branch
now returns Collided; pinned by
SlideSphere_OpposingNormals_ReturnsCollided_WithReversedDisplacementNormal
(RED->GREEN).
- Dat-backed replay (Issue137CorridorSeamReplayTests) reproduces the
live hit frame verbatim (same in/out to the millimeter, same 016E->017A
transit, same +8mm settle) and runs the corridor CLEAN: hit=no, no
sliding normal persisted, six further forward frames advance freely.
- Inspection tests extended: physics-BSP leaf membership walk +
hit-normal candidate sweep + downward-poly sweep (all report-style,
dat-gated). Pickup prompt banner'd SUPERSEDED; ISSUES #137 updated
(door half stays open); audit doc extended with the resolution.
Suites: Core 2551 / App 713 / UI 425 / Net 385, 0 failures.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a11df5b8d3
commit
e8651b3819
7 changed files with 581 additions and 14 deletions
|
|
@ -172,6 +172,42 @@ public class Issue137SlidingNormalLifecycleTests
|
|||
Assert.Equal(TransitionState.Collided, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>CSphere::slide_sphere</c>'s opposing-normals branch (collision
|
||||
/// normal anti-parallel to the contact plane — e.g. a ceiling-facing
|
||||
/// normal while grounded) records the REVERSED displacement as the
|
||||
/// collision normal and returns <b>COLLIDED_TS</b> — retail 0x00537440
|
||||
/// @0x005375d7-0x0053762c: <c>*normal = −gDelta; normalize;
|
||||
/// set_collision_normal; return 2</c>. Our port returned OK (its comment
|
||||
/// even claimed "retail returns OK here"), letting the step complete
|
||||
/// as-is with a synthetic reversed-movement collision normal — the exact
|
||||
/// signature of the live corridor hit (`hit=yes n=(−1.00,0.03,−0.03)` =
|
||||
/// the negated run direction, matching NO dat polygon).
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SlideSphere_OpposingNormals_ReturnsCollided_WithReversedDisplacementNormal()
|
||||
{
|
||||
var t = new Transition();
|
||||
t.SpherePath.InitPath(
|
||||
new Vector3(0f, 0f, 0.2f), new Vector3(0.3f, 0f, 0.2f),
|
||||
0xA9B40001u, BSPStepUpFixtures.SphereRadius);
|
||||
t.CollisionInfo.SetContactPlane(new Plane(Vector3.UnitZ, 0f), 0xA9B40001u, false);
|
||||
|
||||
// Make gDelta exactly (0.4, 0, 0): currPos = check sphere − (0.4,0,0).
|
||||
var currPos = t.SpherePath.GlobalSphere[0].Origin - new Vector3(0.4f, 0f, 0f);
|
||||
|
||||
// Downward collision normal vs the +Z contact plane → cross ≈ 0
|
||||
// (parallel), dot = −1 < 0 (opposing) → the reverse branch.
|
||||
var result = t.SlideSphereInternal(new Vector3(0f, 0f, -1f), currPos);
|
||||
|
||||
Assert.Equal(TransitionState.Collided, result);
|
||||
Assert.True(t.CollisionInfo.CollisionNormalValid);
|
||||
Assert.True(t.CollisionInfo.CollisionNormal.X < -0.99f,
|
||||
$"Collision normal must be the normalized reversed displacement " +
|
||||
$"(−1,0,0); got ({t.CollisionInfo.CollisionNormal.X:F3}," +
|
||||
$"{t.CollisionInfo.CollisionNormal.Y:F3},{t.CollisionInfo.CollisionNormal.Z:F3}).");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Engine-level lifecycle pin — the retail persist/absorb/clear cycle at a
|
||||
// REAL wall. Guards the fix against regressing wall behavior, and
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue