fix #172: port the retail CCylSphere collision family (platform step-up)

The Holtburg town-network portal platform (stab 0xC0A9B465, Setup
0x020019E3, CylSphere r=2.597m h=0.256m) blocked the player with an
endless rim slide instead of retail's step-up-onto-top — gating the
whole #137 dungeon repro. Surfaced when #149 started registering
BSP-less stab CylSpheres: the collision SHAPE became right while the
RESPONSE was still the hand-rolled AP-6 approximation (step-up gate +
radial wall-slide only).

Root cause: no cylinder-TOP support anywhere. DoStepUp's internal
step-down probe needs retail's step_sphere_down (0x0053a9b0) to land on
the flat top — a cylinder has no polygons for the walkable search — so
every step-up onto a wide cylinder failed into StepUpSlide and the
player orbited the rim (probe-confirmed: [cyl-test] result=Slid with
horizontal rim normals, launch-137-repro.log).

Port the full family verbatim: dispatcher intersects_sphere 0x0053b440
(placement/ethereal detection, step-down cap landing, walkable probe,
grounded step_sphere_up 0x0053b310, PathClipped collide_with_point
0x0053acb0, airborne land_on_cylinder 0x0053b3d0, Collide-flag
exact-TOI cap rest) + collides_with_sphere 0x0053a880 +
normal_of_collision 0x0053ab50 + slide_sphere 0x0053b2a0. Pseudocode +
settled BN x87 ambiguities (via ACE cross-ref) + two ACE bugs found and
NOT copied (head-slide foot-disp; see doc §8):
docs/research/2026-07-05-ccylsphere-collision-family-pseudocode.md

Ethereal cylinders now flow through retail's Layer-2 override
(pc:276961) instead of the early-OK consume — same net #150 behavior,
plus retail placement-blocked-by-cylinder semantics. SlideSphere gains
a sphereNum param (retail slides the head sphere by its own
displacement, 0x0053b843).

Register: AP-6 retired; AP-83 added (PerfectClip TOI tail decoded per
ACE, dead code until missiles). Tests: CylSphereFamilyTests (grounded
step-up onto the exact platform shape, tall-cylinder block, airborne
top landing, ethereal guard); the #42 self-shadow control assertion
updated to the retail observable (denied movement — the old ~1m radial
self-push was the approximation's artifact, not retail). Suites: Core
2533 / App 713 / UI 425 / Net 385 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-05 14:52:28 +02:00
parent 67c3357246
commit 6ab269894a
6 changed files with 1027 additions and 151 deletions

View file

@ -46,6 +46,56 @@ Copy this block when adding a new issue:
---
## #172 — Town-network portal platform blocks instead of stepping up (CCylSphere family was never ported)
**Status:** 🟡 FIX SHIPPED 2026-07-05 (this commit) — pending user visual gate (walk up onto the Holtburg portal platform, then the 0x0007 dungeon run).
**Severity:** HIGH (blocks dungeon access — gates the whole #137 repro)
**Filed:** 2026-07-05
**Component:** physics — CylSphere object collision response
**Description (user):** the Holtburg town-network portal sits on a stone
platform the player collides with instead of stepping up onto it (retail just
walks up). Entity `0xC0A9B465` = landblock stab #0x65, Setup `0x020019E3`,
one CylSphere **r=2.597 m, h=0.256 m** — a 26 cm disc, trivially steppable in
retail. Surfaced the moment #149 (`4cf6eeb`) started registering BSP-less
stab CylSpheres (before that fix the platform had NO collision at all, so the
player clipped through it — the collision *shape* was the #149 fix; the
collision *response* was never retail).
**Root cause (probe-confirmed, `launch-137-repro.log`):** the pre-port
`CylinderCollision` was a hand-rolled approximation (AP-6): step-up gate +
radial wall-slide only. Every contact returned `Slid` with a horizontal rim
normal (`[cyl-test] … result=Slid`, `[resolve] … n=(0.99,-0.11,0.00)`) and
the player orbited the rim forever. The step-up *gate* passed (clearance
0.256 ≤ 0.6) but `DoStepUp`'s internal step-down probe could never validate a
landing ON the cylinder top — a cylinder has no polygons, and the port had no
`step_sphere_down` cap-landing (top-disc contact plane). Airborne landings on
tops (`land_on_cylinder` + the Collide-flag exact-TOI branch) were missing
too.
**Fix (this commit):** verbatim port of the full retail `CCylSphere` family —
dispatcher `intersects_sphere` 0x0053b440, `collides_with_sphere` 0x0053a880,
`normal_of_collision` 0x0053ab50, `collide_with_point` 0x0053acb0,
`slide_sphere` 0x0053b2a0, `step_sphere_up` 0x0053b310, `land_on_cylinder`
0x0053b3d0, `step_sphere_down` 0x0053a9b0. Pseudocode + settled BN
ambiguities + two ACE-bug findings:
`docs/research/2026-07-05-ccylsphere-collision-family-pseudocode.md`.
Register: AP-6 retired, AP-83 added (PerfectClip TOI tail per ACE, dead code
in M1.5). Conformance: `CylSphereFamilyTests` (grounded step-up-onto-top on
the exact platform shape, tall-cylinder block, airborne top landing, ethereal
Layer-2 guard); the #42 self-shadow control assertion updated to the retail
observable (denied movement, not the old artifact radial push).
**Files:** `src/AcDream.Core/Physics/TransitionTypes.cs` (`CylinderCollision`
+ `Cyl*` family), `tests/AcDream.Core.Tests/Physics/CylSphereFamilyTests.cs`.
**Acceptance:** walk straight onto the Holtburg town-network portal platform
(no rim slide); jumping onto it also lands. Doors/torches/NPC cylinders
unregressed (suites green; #150 open-door behavior unchanged). Likely also
advances #137's door-foot half — re-check in the dungeon repro.
---
## #171 — Group melee: monsters interpenetrate + facing drifts (sticky melee unbound, arrival radii = 0)
**Status:** DONE (2026-07-04) — **user visual gate PASSED** ("Looks good, ship it")