test(physics): harden TS-4 production chronology

This commit is contained in:
Erik 2026-07-31 14:08:51 +02:00
parent 75b6f6b6c9
commit d3c0d9ec0e
5 changed files with 807 additions and 101 deletions

View file

@ -599,17 +599,16 @@ manufacture a caller that sets it.
### The former shortcut (HISTORICAL FACT; removed by Slice 2B)
Before Slice 2B, Path-6 (the default
`sphere_intersects_poly → collide_with_pt / SetCollide` dispatch) tested each
hit polygon's world-space normal. For BOTH sphere0 (feet) and sphere1 (head),
if `worldNormal.Z < PhysicsGlobals.FloorZ` (steeper than ~49° from
horizontal), acdream took a SPECIAL BRANCH:
projects the move along the steep face, writes
`collisions.SetCollisionNormal(worldNormal)` **and**
`collisions.SetSlidingNormal(worldNormal)`, and returns
`TransitionState.Slid` immediately — bypassing `SetCollide` entirely for
steep hits. Only the shallow case (`worldNormal.Z >= FloorZ`) reaches
`path.SetCollide(worldNormal); path.WalkableAllowance = LandingZ; return
Adjusted;`.
`sphere_intersects_poly → collide_with_pt / SetCollide` dispatch) was not
symmetrically wrong. The parsed-graph primary/foot branch tested the hit
polygon's world-space normal and, below `PhysicsGlobals.FloorZ`, projected
the move along the face, wrote both collision and sliding normals, and
returned `Slid`. Its secondary/head branch had already been corrected by
#116 to retail's unconditional `CollisionNormal` + `Collided` response. The
prepared-flat port still applied the steep shortcut to both spheres and sent
both shallow cases through `SetCollide` + `Adjusted`; its head branch was
therefore wrong for every slope. This distinction matters: retail does not
apply one common response to both spheres.
The in-code comment is unusually candid about why: **"This is a
deliberate deviation from retail... Validated against retail debugger
@ -621,10 +620,11 @@ was shipped SAME-DAY as (and BECAUSE) the retail-faithful
§2 above — also dated 2026-04-30, tagged "L.4") still wedged in testing
when tried without this shortcut.
### Retail: NO steepness branch at the BSP layer (FACT, pseudo-C:323740-323783, `0053a730` region)
### Retail: NO steepness branch in either Path-6 sphere response (FACT, `0053a730` region)
Read directly from the named decomp (the `sphere_intersects_poly` /
`set_collide` dispatch inside `BSPTREE::find_collisions`'s default path):
`set_collide` dispatch for the primary/foot sphere inside
`BSPTREE::find_collisions`'s default path):
```
if (sphere_intersects_poly(...) || eax_26 != 0) {
@ -635,11 +635,21 @@ if (sphere_intersects_poly(...) || eax_26 != 0) {
}
```
**There is no steepness test here at all.** Retail's BSP layer calls
`set_collide` and returns `ADJUSTED_TS` **unconditionally**, for a steep
roof exactly the same as a shallow ramp. `walkable_allowance` is always
set to `LandingZ` (the permissive landing threshold) at this layer,
regardless of the actual polygon slope. This directly confirms the P2
**There is no steepness test in this foot branch.** It calls `set_collide`
and returns `ADJUSTED_TS` for a steep roof exactly as for a shallow ramp.
`walkable_allowance` is always set to `LandingZ` (the permissive landing
threshold), regardless of polygon slope. If the foot is clear but the
secondary/head sphere hits, retail instead performs the other slope-agnostic
response:
```
localtoglobalvec(sphere_path.localspace_pos, &normal, &head_poly->plane.N);
COLLISIONINFO::set_collision_normal(&collision_info, &normal);
return 2; // COLLIDED_TS
```
The head branch neither calls `set_collide` nor returns `ADJUSTED_TS`.
Neither sphere response has a steepness branch. This directly confirms the P2
plan's description and the digest's #137-mechanism-2 finding
(`memory/project_physics_collision_digest.md:1008-1014`): **retail's
BSP/sphere collision layer never writes `collision_info.sliding_normal`
@ -653,16 +663,13 @@ domain, §2 above).
### TS-4 port shape (COMPLETED 2026-07-31)
Delete both `if (worldNormal{0,1}.Z < PhysicsGlobals.FloorZ) { ... return
TransitionState.Slid; }` blocks (`BSPQuery.cs:2200-2215` and
`:2240-2255`) entirely. Both sphere0 and sphere1 hits should fall straight
through to the existing `path.SetCollide(worldNormal); path.WalkableAllowance
= PhysicsGlobals.LandingZ; return TransitionState.Adjusted;` — i.e., make
Path-6 do EXACTLY what its own shallow branch already does, for every
hit, matching retail's unconditional `set_collide`. This mechanically
retires both `SetSlidingNormal` write sites (satisfying DO-NOT-RETRY §0
item 1 permanently — deleted, not just avoided) with no replacement logic
needed at this layer.
Delete the primary/foot steep shortcut in both representations. Every foot
hit must call `SetCollide`, set `WalkableAllowance=LandingZ`, and return
`Adjusted`. Preserve the parsed-graph head branch corrected by #116, and
replace the prepared-flat head steep/shallow split with the same unconditional
`SetCollisionNormal` + `Collided` response. No Path-6 branch writes a sliding
normal. This is the exact retail foot/head split, not a shared fallback to the
foot behavior.
### Port-order coupling with TS-1 (historical guard, now satisfied)
@ -1202,17 +1209,35 @@ representations by raw bits and pin every mutated and preserved field:
- both: a pre-existing sliding normal is preserved byte-for-byte.
The failed first removal was a test-harness lesson, not a retail exception.
Its gravity-only replay always passed `isOnGround:false` and discarded each
accepted result's Contact/OnWalkable bits, making the nested edge/StepDown
chain impossible to exercise on the next frame. The replacement replay uses
the same retained `PhysicsBody` chronology as production. Parsed graph and
prepared flat now match by raw result/body bits for vertical roof descent,
downhill input, uphill pressure (including a no-launch/no-bounce assertion),
tangential roof travel, inward-plus-tangent wall travel, and flat-roof ledge
rejection. Every trace asserts finite bounded motion and signed-plane non-
penetration. The former #116 D4 control is active: its primary-sphere hit
hard-stops frame one through SetCollide, then the accepted persistent normal
permits the downward slide on frame two.
Its gravity-only replay always passed `isOnGround:false`, manually zeroed
vertical velocity, and only copied part of the accepted contact state, so it
could not reproduce the live caller's next-frame chronology. The replacement
`Ts4ProductionQuantumConformanceTests` replay executes each already-airborne,
zero-root-motion 30 Hz Core collision quantum in the production order:
`calc_acceleration`, `UpdatePhysicsInternal`,
`ResolveWithTransition`, exact body/cell commit, then
`PhysicsObjUpdate.CommitSetPositionTransition` (the Core owner of retail's
Contact/OnWalkable replacement plus `handle_all_collisions`). It carries the
same body, cell, contact plane, walkable plane, sliding state, stationary-fall
counter, cached velocity, and transient flags for all 90 ticks. PositionManager
root composition, animation hooks, movement callbacks, and fresh-airborne
`LeaveGround`/`HitGround` edges are deliberately outside this already-airborne
dat-free fixture; it does not claim to replay those presentation/motion stages.
Parsed graph and prepared flat match by raw result/body bits for vertical,
inward, tangential, and downhill roof motion plus a genuine positive-Z uphill
jump. The foot-contact jump's apex precedes roof contact and every later
candidate velocity remains non-positive in Z. A separate elevated head-only
collision reaches the wall while Z velocity is still positive and pins the
valid-normal, inward-dot retail 5%-elastic reflection without adding vertical
velocity. Every direction rejects a half-second fixed point and signed-plane
penetration, while exact terminal velocity, Contact/OnWalkable/Sliding bits,
sliding normal, and contact plane are fixed by raw float bits. The older
resolver-only wedge capture remains a deliberately weaker historical control
and is restored to its original three-second bound. The former #116 D4
control remains active: its primary-sphere hit hard-stops frame one through
SetCollide, then the accepted persistent normal permits the downward slide on
frame two.
The complete historical #273/#271/#269/#265/#185/#137/#116/cellar/roof
matrix and the full Core Release suite pass without a replacement