fix(physics): #32 L.2c precipice edge-slide context
Port the first retail precipice-slide slice from named retail/ACE: terrain and BSP walkable hits now preserve polygon vertices, failed step-down edges back-probe to rediscover the walkable polygon, and edge-slide can run precipice/cliff slide instead of only hard-stopping. Adds pseudocode anchors plus regression coverage for terrain polygon context and loaded-terrain boundary edge-slide. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
1ec40f2a4f
commit
261322b48e
10 changed files with 559 additions and 60 deletions
|
|
@ -189,19 +189,22 @@ step-down boundaries, retail often slides along the boundary. acdream still
|
|||
hard-blocks or accepts too much in several of these cases.
|
||||
|
||||
**Root cause / status:** Tracked under Phase L.2c. Wall-adjacent
|
||||
`step_up_slide` now feels acceptable in live testing. L.2c plumbing now passes
|
||||
the retail-default `EdgeSlide` flag into local and remote movement and logs
|
||||
failed step-down edge cases behind `ACDREAM_DUMP_EDGE_SLIDE=1`. Remaining gap:
|
||||
preserve walkable polygon context for `precipice_slide` and finish
|
||||
`cliff_slide` / `NegPolyHit` dispatch. Named retail anchors include
|
||||
`CTransition::edge_slide`, `CTransition::cliff_slide`,
|
||||
`SPHEREPATH::precipice_slide`, and `SPHEREPATH::step_up_slide`.
|
||||
`step_up_slide` now feels acceptable in live testing. Local/remote movement
|
||||
passes the retail-default `EdgeSlide` flag. The first precipice-slide slice now
|
||||
preserves terrain/BSP walkable polygon vertices and runs the retail back-probe
|
||||
before `SPHEREPATH::precipice_slide`; `ACDREAM_DUMP_EDGE_SLIDE=1` now reports
|
||||
whether a failed step-down had polygon context. Remaining gaps: real-DAT
|
||||
building-edge fixtures, fuller `cliff_slide` coverage, and `NegPolyHit`
|
||||
dispatch. Named retail anchors include `CTransition::edge_slide`,
|
||||
`CTransition::cliff_slide`, `SPHEREPATH::precipice_slide`, and
|
||||
`SPHEREPATH::step_up_slide`.
|
||||
|
||||
**Files:** `src/AcDream.Core/Physics/TransitionTypes.cs`,
|
||||
`src/AcDream.Core/Physics/BSPQuery.cs`,
|
||||
`tests/AcDream.Core.Tests/`.
|
||||
|
||||
**Research:** `docs/plans/2026-04-29-movement-collision-conformance.md`.
|
||||
**Research:** `docs/plans/2026-04-29-movement-collision-conformance.md`,
|
||||
`docs/research/2026-04-30-precipice-slide-pseudocode.md`.
|
||||
|
||||
**Acceptance:** Synthetic and real-DAT tests cover wall-slide, roof-edge slide,
|
||||
cliff/precipice slide, failed step-up/step-down, and the jump-clears-edge case.
|
||||
|
|
|
|||
|
|
@ -118,6 +118,13 @@ precipices.
|
|||
edge, walkable, and collision rules; jumping clears `OnWalkable` and only
|
||||
succeeds when the airborne path actually clears geometry.
|
||||
|
||||
Current shipped slice (2026-04-30): wall-adjacent `step_up_slide` feels
|
||||
acceptable in live testing; player/remote movers pass `EdgeSlide`; terrain and
|
||||
BSP step-down/find-walkable now preserve walkable polygon vertices; failed
|
||||
step-down edge cases perform the retail back-probe before
|
||||
`SPHEREPATH::precipice_slide`. Remaining L.2c work is real-DAT building-edge
|
||||
fixtures, fuller `cliff_slide` coverage, and `NegPolyHit` dispatch.
|
||||
|
||||
### L.2d - Shape Fidelity: Sphere / CylSphere / Building Objects
|
||||
|
||||
Goal: object collisions use retail shape semantics, not one simplified
|
||||
|
|
|
|||
110
docs/research/2026-04-30-precipice-slide-pseudocode.md
Normal file
110
docs/research/2026-04-30-precipice-slide-pseudocode.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# Precipice Slide Pseudocode
|
||||
|
||||
Date: 2026-04-30
|
||||
|
||||
Phase: L.2c - Movement & Collision Conformance
|
||||
|
||||
## Retail Anchors
|
||||
|
||||
- Named retail: `CTransition::edge_slide`, `acclient_2013_pseudo_c.txt:273001`
|
||||
- Named retail: `CTransition::cliff_slide`, `acclient_2013_pseudo_c.txt:272397`
|
||||
- Named retail: `SPHEREPATH::precipice_slide`, `acclient_2013_pseudo_c.txt:274316`
|
||||
- ACE cross-check: `Transition.EdgeSlide`, `Transition.CliffSlide`,
|
||||
`SpherePath.PrecipiceSlide`
|
||||
- ACE cross-check: `Polygon.find_crossed_edge`
|
||||
|
||||
## Edge-Slide Flow
|
||||
|
||||
When a grounded mover has contact state but the next candidate position has no
|
||||
walkable surface within step-down reach, retail does not immediately accept the
|
||||
fall or hard-stop. It enters `CTransition::edge_slide`.
|
||||
|
||||
```text
|
||||
edge_slide(transitionState, stepDownHeight, walkableZ):
|
||||
if object is not OnWalkable or EdgeSlide is disabled:
|
||||
clear walkable
|
||||
restore candidate check position
|
||||
clear current contact plane
|
||||
mark cell array valid
|
||||
transitionState = OK
|
||||
return handled
|
||||
|
||||
if current collision has a contact plane below walkableZ:
|
||||
transitionState = cliff_slide(contact plane)
|
||||
clear walkable and restore candidate check position
|
||||
clear current contact plane
|
||||
return not-final
|
||||
|
||||
if sphere_path.walkable exists:
|
||||
transitionState = precipice_slide()
|
||||
clear current contact plane and restore candidate check position
|
||||
return transitionState == Collided
|
||||
|
||||
if current collision has any contact plane:
|
||||
clear walkable
|
||||
restore candidate check position
|
||||
clear current contact plane
|
||||
transitionState = OK
|
||||
return handled
|
||||
|
||||
move CheckPos back from failed candidate to the current sphere center
|
||||
step_down(stepDownHeight, walkableZ) to rediscover the walkable polygon
|
||||
clear current contact plane
|
||||
restore the failed candidate check position
|
||||
|
||||
if a walkable polygon was discovered:
|
||||
set walkable_check_pos from the candidate sphere in walkable space
|
||||
transitionState = precipice_slide()
|
||||
return transitionState == Collided
|
||||
|
||||
clear walkable
|
||||
mark cell array valid
|
||||
transitionState = Collided
|
||||
return handled
|
||||
```
|
||||
|
||||
## Precipice Slide
|
||||
|
||||
`SPHEREPATH::precipice_slide` is the edge-normal half of edge-slide. The crucial
|
||||
input is the walkable polygon that the mover just left; without that polygon,
|
||||
there is no crossed edge to slide along.
|
||||
|
||||
```text
|
||||
precipice_slide():
|
||||
normal = zero
|
||||
found = walkable.find_crossed_edge(walkable_check_pos, walkable_up, normal)
|
||||
|
||||
if not found:
|
||||
clear walkable
|
||||
return Collided
|
||||
|
||||
clear walkable
|
||||
step_up = false
|
||||
|
||||
normal = walkable_pos.frame.LocalToGlobalVec(normal)
|
||||
|
||||
blockOffset = LandDefs.GetBlockOffset(curr cell, check cell)
|
||||
movementOffset = global_sphere.center - global_curr_center.center + blockOffset
|
||||
|
||||
if dot(normal, movementOffset) > 0:
|
||||
normal = -normal
|
||||
|
||||
return global_sphere.slide_sphere(transition, normal, global_curr_center.center)
|
||||
```
|
||||
|
||||
## Porting Notes
|
||||
|
||||
acdream already had the `Polygon.find_crossed_edge` math inside `BSPQuery`, but
|
||||
the live diagnostic showed `walkableValid=False` at the failed step-down edge
|
||||
branch. The port must therefore preserve or rediscover the walkable polygon,
|
||||
not just pass the `EdgeSlide` flag.
|
||||
|
||||
For the first L.2c slice:
|
||||
|
||||
- terrain supplies the exact current triangle vertices alongside its plane;
|
||||
- BSP step-down/find-walkable records world-space polygon vertices when the
|
||||
caller supplies the object's world origin;
|
||||
- the failed step-down edge branch performs the retail back-probe to current
|
||||
position before calling precipice slide;
|
||||
- `CELLARRAY`, full `cell_bsp` ownership, and cross-cell building portals remain
|
||||
L.2e work.
|
||||
Loading…
Add table
Add a link
Reference in a new issue