fix(physics): enforce retail step-down support radius (#273)
This commit is contained in:
parent
4dd40ad8fe
commit
c24bc571cf
9 changed files with 2117 additions and 24 deletions
95
docs/research/2026-07-31-issue273-tight-gap-support.md
Normal file
95
docs/research/2026-07-31-issue273-tight-gap-support.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# Issue #273 — Holtburg tight-gap support validation
|
||||
|
||||
**Date:** 2026-07-31
|
||||
**Status:** implementation, automated gates, and exact live gate pass
|
||||
**Scope:** grounded player step-down support at a floor edge beside a static
|
||||
cylinder
|
||||
|
||||
## Captured scene
|
||||
|
||||
The reproducible gap is in outdoor cell `0xA9B40032`, between:
|
||||
|
||||
- building shell GfxObj `0x01000F69`, placed at
|
||||
`(158.178, 37.7055, 94.0)` with quaternion
|
||||
`(w=.939319, x=0, y=0, z=-.343045)`;
|
||||
- static post `0xCA9B4027`, placed at `(160.173, 34.487, 95.975)`,
|
||||
represented by its Setup-authored cylinder (`radius=.282`,
|
||||
`height=5.564`);
|
||||
- the local player Setup's exact two spheres (`radius=.48`, origins
|
||||
`z=.475` and `z=1.35`).
|
||||
|
||||
The building's supporting ledge terminates at local `x=4`. The first
|
||||
post-side response moved the player's foot-sphere center to approximately
|
||||
local `x=4.33`. The full `.48` movement sphere still overlapped the floor, so
|
||||
the existing step-down path accepted the candidate. Repeated frames then
|
||||
carried the player around the post and outside the building shell.
|
||||
|
||||
The fixture
|
||||
`tests/AcDream.Core.Tests/Fixtures/issue273/0x01000F69.gfxobj.json` preserves
|
||||
the installed DAT PhysicsBSP. The replay in
|
||||
`Issue273HoltburgTightGapReplayTests` uses the captured object placement,
|
||||
player spheres, static posts, and movement offsets.
|
||||
|
||||
## Retail mechanism
|
||||
|
||||
The missing rule is not extra collision padding and is not a larger player
|
||||
sphere. It is retail's second-stage support validation:
|
||||
|
||||
1. `CTransition::step_down` (`0x0050B2A0`) performs the ordinary downward
|
||||
collision probe.
|
||||
2. After finding a walkable contact plane, an EdgeSlide mover that is not in
|
||||
StepUp calls `CTransition::check_walkable` (`0x0050AFF0`). The binary
|
||||
sequence is `test ah,2` at `0x0050B36A`, which is state bit `0x200`
|
||||
(`EdgeSlide`), followed by the `step_up == 0` test and call at
|
||||
`0x0050B380`.
|
||||
3. `CTransition::check_walkable` first calls
|
||||
`SPHEREPATH::check_walkables` (`0x0050C3E0`).
|
||||
4. `SPHEREPATH::check_walkables` halves the saved foot-sphere radius and
|
||||
calls `CPolygon::check_walkable` (`0x00538E60`).
|
||||
5. If the remembered polygon does not support that smaller sphere,
|
||||
`CTransition::check_walkable` performs a downward CheckWalkable insertion.
|
||||
BSP leaves require both `walkable_hits_sphere` and
|
||||
`CPolygon::check_small_walkable` (`BSPLEAF::hits_walkable`,
|
||||
`0x0053D670`).
|
||||
6. If neither check finds support, `CTransition::step_down` rejects the
|
||||
candidate and the existing edge-response chain handles it.
|
||||
|
||||
ACDream already had the small-radius BSP-leaf test, but
|
||||
`DoCheckWalkable` treated the mere presence of a remembered polygon as
|
||||
success, and the ordinary `DoStepDown(..., runPlacement:false)` path never
|
||||
called it. This let a full-radius overlap stand in for actual foot support.
|
||||
|
||||
## Port
|
||||
|
||||
- `BSPQuery.CheckWalkableSupport` is the shared resolved-polygon form of
|
||||
retail `CPolygon::check_walkable`.
|
||||
- `SpherePath.CheckWalkables` implements the retail half-radius remembered
|
||||
polygon check without mutating canonical sphere state.
|
||||
- `Transition.DoCheckWalkable` now tests the remembered polygon rather than
|
||||
treating a non-null polygon as sufficient.
|
||||
- `Transition.DoStepDown` restores the EdgeSlide/non-StepUp support gate
|
||||
before the existing placement-policy seam.
|
||||
|
||||
There are no location checks, object IDs, guessed radii, widened collision
|
||||
shapes, or gap-specific tolerances in the production fix.
|
||||
|
||||
## Regression impact
|
||||
|
||||
The existing #271 staircase-side replay begins with its center `.288 m`
|
||||
outside a tread whose retail half-radius support boundary is `.24 m`.
|
||||
Retail may therefore stop that exact candidate. The test now preserves the
|
||||
original user-visible invariant—never reverse or accelerate downhill—without
|
||||
requiring forward progress beyond retail's support boundary. The ordinary
|
||||
continuous staircase replay still requires and achieves forward progress.
|
||||
|
||||
## Gates
|
||||
|
||||
- issue #273 fixture/replay: 3 passed;
|
||||
- focused BSP, step-up, edge-slide, #185/#271 family: 42 passed / 1 skipped;
|
||||
- complete Core tests: 4,111 passed / 2 skipped;
|
||||
- Release solution build: passed;
|
||||
- complete Release solution tests: 10,068 passed / 5 skipped.
|
||||
|
||||
The user accepted the exact in-client Holtburg gap gate on 2026-07-31: the
|
||||
gap blocks from the tested approach, and the adjacent movement checks remain
|
||||
healthy.
|
||||
Loading…
Add table
Add a link
Reference in a new issue