fix(physics): #116 shape-1 — Path-6 head-sphere direct Collided return
Per docs/research/2026-07-30-ts4-116-oracle-plan.md §2.3-§2.4: retail's
airborne (not-yet-Contact) BSPTREE::find_collisions dispatch, when the
FOOT sphere is completely clear but the HEAD sphere hits or near-misses,
does not defer through SetCollide/Adjusted (nor the steep-poly
slide-tangent shortcut) — it records the head polygon's normal directly
and hard-stops: pc:323824-323834 (0x0053a793/0x0053a7a4), independently
cross-checked against ACE BSPTree.cs:221-230 (`SetCollisionNormal` +
`return TransitionState.Collided;`), an exact structural match confirming
this isn't a BN misdecompile. BSPQuery.cs's Path 6 `hasSphere1` branch now
does the same: `collisions.SetCollisionNormal(worldNormal1); return
TransitionState.Collided;`, replacing the old steep-shortcut-or-deferred-
SetCollide handling. This mechanically retires one of TS-4's two
`SetSlidingNormal` write sites (sphere1's) ahead of TS-4's own item.
Added two permanent diagnostics gated on the existing
PhysicsDiagnostics.ProbeIndoorBspEnabled flag (`[path-dispatch]` at
FindCollisionsCore entry, `[path5-diag]` inside Path 5) to make future
BSPQuery dispatch tracing cheaper.
HONEST RESULT of the plan's own confirming instrumentation (re-run of
DoorBugTrajectoryReplayTests.Diagnostic_Tick22760_DumpEngineInternals):
this fix does NOT change the tick-22760 outcome (harness still cn=(0,0,1)
vs live cn=(0,+1,0)). The new dispatch-entry probes show the tick-22760
mover is GROUNDED (Contact set), so it never reaches Path 6 at all — it
dispatches Path 5 -> StepSphereDown (Path 3, both DoStepDown half-steps
fail) -> EdgeSlideAfterStepDownFailed -> SpherePath.PrecipiceSlide, whose
find_crossed_edge-false fallback returns Collided with NO collision-normal
write. A fresh byte-level read of retail's SPHEREPATH::precipice_slide
(pc:274316-274326, 0x0050cc80) confirms this is byte-exact retail
behavior (`if (eax == 0) { walkable = 0; return 2; }`, no
set_collision_normal call) — not a bug. The real tick-22760 divergence is
further upstream, most likely this test's simplified door registration
(BuildEngineWithDoorFixture) not placing the door's BSP where live retail
actually intersected it, or a walkable-polygon state-capture gap — see
the research doc's Addendum 2 for the full trace and open candidates.
#116 shape-1 is therefore NARROWED, not closed: the Path-6 fix is a real,
independent retail-faithfulness improvement; the tick-22760 acceptance
criterion is not met by it and needs further harness/geometry work before
any further code change.
Full AcDream.Core.Tests suite: 4059 passed / 2 skipped, no regressions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
7e1be3def0
commit
db2889afda
2 changed files with 158 additions and 22 deletions
|
|
@ -640,3 +640,102 @@ within 10° of flat; the shipped ACE-derived constant engages only within
|
|||
the TS-4/#116 implementation slice, which owns `PhysicsBody`): replace
|
||||
`0.99999536f` with `0.98480775f` (cos 10°), cite this addendum, retire
|
||||
register row AD-55 in the same commit.
|
||||
|
||||
## Addendum 2 (implementation session, 2026-07-30): #116 shape-1's tick-22760
|
||||
## confirming run DISAGREES with this plan's hypothesis — the real mechanism
|
||||
## is one layer further upstream, and it's retail-faithful there too
|
||||
|
||||
Per this doc's own §4 execution order, item 1 (the `TransitionalInsert`
|
||||
exhausted-loop hardcoded return) landed first — mechanical, zero
|
||||
observable behavior change, confirmed by the full `AcDream.Core.Tests`
|
||||
suite (4059 passed / 2 skipped, no change in pass count). Then §2.3's
|
||||
shape-1 fix landed verbatim in `BSPQuery.cs`'s Path 6 `hasSphere1`
|
||||
branch: a foot-clear/head-hit airborne contact now returns
|
||||
`TransitionState.Collided` with a direct `collisions.SetCollisionNormal`
|
||||
write, exactly matching pc:323824-323834 (`0x0053a793`/`0x0053a7a4`) and
|
||||
ACE `BSPTree.cs:221-230`. This is a real, independently-decomp-confirmed
|
||||
port-accuracy fix and is kept regardless of the result below.
|
||||
|
||||
**The confirming instrumentation run (§2.4) DISAGREES with the plan's
|
||||
tick-22760 hypothesis.** Re-running
|
||||
`DoorBugTrajectoryReplayTests.Diagnostic_Tick22760_DumpEngineInternals`
|
||||
after the fix landed shows **no change**: harness still reports
|
||||
`cn=(0,0,1)` (the `UnitZ` ground-fallback default) against live's
|
||||
`cn=(0,+1,0)` (the door-face normal). Adding a dispatcher-entry probe
|
||||
(`[path-dispatch]`, `[path5-diag]`, gated on the existing
|
||||
`ProbeIndoorBspEnabled` flag, kept in `BSPQuery.cs` as permanent
|
||||
diagnostics) traced the ACTUAL call sequence for this capture:
|
||||
|
||||
1. The seeded body's `TransientState` (131 = `Contact | OnWalkable |
|
||||
Active`) means `ObjectInfo.State & Contact != 0` for this mover — it
|
||||
is **grounded**, so `BSPQuery.FindCollisionsCore` dispatches to
|
||||
**Path 5** (the Contact/grounded branch), never Path 6 at all. The
|
||||
plan's shape-1 hypothesis was explicitly scoped to "the not-yet-in-
|
||||
Contact branch" (§2.3) — that scoping was itself the unconfirmed
|
||||
part, and it does not hold for tick-22760.
|
||||
2. Path 5's own dispatch for the door's BSP shape at this exact position
|
||||
finds **neither sphere hitting nor near-missing**
|
||||
(`hit0=False hitPoly0=False hit1=False hitPoly1=False`) — the
|
||||
simplified fixture registration this test uses
|
||||
(`BuildEngineWithDoorFixture`, which places the raw GfxObj BSP
|
||||
directly at its captured world-space bounding-sphere center rather
|
||||
than via the faithful `ShadowShapeBuilder.FromSetup` +
|
||||
`PlacementFrame` transform that `BuildFaithfulDoorEngine` uses
|
||||
elsewhere in the same file) returns `OK` for the door here.
|
||||
3. `TransitionalInsert`'s step-down gate then fires
|
||||
(`contactInvalidOrSteep` is true because the per-substep walk loop
|
||||
clears `ContactPlaneValid` before every `TransitionalInsert` call —
|
||||
`TransitionTypes.cs` around the `FindValidPosition` per-step reset —
|
||||
so the door BSP is queried TWICE MORE via `DoStepDown`'s two half-
|
||||
height attempts, dispatching to **Path 3** (`StepSphereDown` →
|
||||
`FindWalkableInternal`), which also finds no walkable candidate here
|
||||
(the door face is not a floor-like polygon) and returns `OK` both
|
||||
times.
|
||||
4. Both `DoStepDown` calls therefore fail (return `false`), which routs
|
||||
into `EdgeSlideAfterStepDownFailed`. With `ContactPlaneValid` false,
|
||||
`OnWalkable` true (seeded), `EdgeSlide` true (mover flags), and the
|
||||
RESTORED walkable polygon from the body's own snapshot (a flat
|
||||
triangle `(144,0,94)-(144,24,94)-(120,24,94)`, `Normal.Z=1 >=
|
||||
FloorZ`), execution reaches `sp.PrecipiceSlide(this)`
|
||||
(`TransitionTypes.cs` "branch3/precipice-slide").
|
||||
5. **`SpherePath.PrecipiceSlide` calls `BSPQuery.FindCrossedEdge` against
|
||||
that seeded triangle. The player's actual sweep (X≈133, Y from 18.02
|
||||
to 17.60) does not cross ANY of that triangle's three edges** (the
|
||||
triangle spans roughly X∈[120,144], and its hypotenuse sits at
|
||||
X+Y=144 — at X=133 that's Y≈11, far south of the player's Y range).
|
||||
`FindCrossedEdge` returns false, and acdream's `PrecipiceSlide`
|
||||
(`TransitionTypes.cs:1039-1054`) does exactly what retail's
|
||||
`SPHEREPATH::precipice_slide` does on the identical branch — **read
|
||||
fresh this session, pc:274316-274326, `0x0050cc80`**:
|
||||
```
|
||||
int32_t eax = CPolygon::find_crossed_edge(...);
|
||||
if (eax == 0) { this->walkable = eax; return 2; /* COLLIDED_TS */ }
|
||||
```
|
||||
**No `set_collision_normal` call on this path in retail either.**
|
||||
This is a byte-exact match, not an inference — acdream's
|
||||
`ClearWalkable(); return TransitionState.Collided;` on a failed
|
||||
`FindCrossedEdge` is retail-faithful. `ValidateTransition`'s
|
||||
`UnitZ`-default-on-invalid-normal fires identically in both engines
|
||||
for this exact mechanism.
|
||||
|
||||
**Conclusion: the tick-22760 divergence is NOT explained by anything this
|
||||
plan identified, and the mechanism this pass traced down to (Path 5 →
|
||||
StepSphereDown → EdgeSlideAfterStepDownFailed → PrecipiceSlide's
|
||||
no-crossed-edge fallback) is independently confirmed retail-faithful at
|
||||
every step, including a fresh byte-level read of `precipice_slide`
|
||||
itself.** The remaining candidates, none guessed at here: (a) this
|
||||
specific harness (`BuildEngineWithDoorFixture`) may simply not place the
|
||||
door's BSP polygons where live retail's did at that exact tick — a
|
||||
harness/fixture-geometry gap, not a response-layer code bug — worth
|
||||
re-running this same capture through `BuildFaithfulDoorEngine`'s
|
||||
Setup-based registration to check whether a REAL BSP hit against the
|
||||
door (rather than the seeded generic floor triangle) changes the
|
||||
outcome; (b) the seeded `WalkableVertices` triangle itself may not match
|
||||
what retail's own walkable-polygon bookkeeping held at that instant
|
||||
(a state-capture gap in the original 2026-05-24 live-capture tooling,
|
||||
not necessarily an engine bug); (c) a genuinely different upstream
|
||||
mechanism not yet traced. Per CLAUDE.md's no-guessing rule, none of
|
||||
these is adopted without further evidence — #116 shape-1 stays
|
||||
**narrowed, not closed**: the Path-6 fix is a real, independent
|
||||
retail-faithfulness improvement, and the original tick-22760 acceptance
|
||||
criterion is NOT met by it. See ISSUES.md #116 for the updated status.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue