fix(physics): Task 3 follow-up — obstruction_ethereal consume for Cylinder+Sphere shapes
Retail oracle greps confirmed: - CSphere::intersects_sphere @ 0x00537ae4 (pc:321692): the ethereal branch is `void __thiscall` — all paths return void (no COLLIDED). The function performs a proximity check only; no blocking result is produced. - CCylSphere::intersects_sphere @ 0x0053b4a0 (pc:324573): same void-return pattern — ethereal branch calls collides_with_sphere (check only, no slide), all returns are void = passable. Change: added `if (sp.ObstructionEthereal) return TransitionState.OK` at the top of SphereCollision and CylinderCollision in TransitionTypes.cs, mirroring the void-return semantics of both retail functions. The existing per-object clear at pc:276989 (line 2837) still fires after the early OK return. Before this fix: an ethereal-alone NPC/ghost with a Cylinder or Sphere shadow shape would BLOCK the player (regression introduced when Task 3 made ETHEREAL- alone fall through ShouldSkip instead of instant-skipping). After: all three shape types — BSP (via BSPQuery Path 1), Sphere, and Cylinder — correctly pass through when obstruction_ethereal is set. Tests: added 4 tests to ObstructionEtherealTests.cs verifying: - Ethereal Cylinder → passable (sweep passes through, no CollisionNormalValid) - Ethereal Sphere → passable (same) - Non-ethereal Cylinder → still blocks (regression guard) - Non-ethereal Sphere → still blocks (regression guard) Full Core suite: 1584 pass, 0 fail, 2 skip (pre-existing dat skips). Pseudocode doc updated with confirmed cyl/sphere ethereal contracts and the complete set/clear/consume flow summary. Retail refs: - CSphere::intersects_sphere @ 0x00537ae4 / acclient_2013_pseudo_c.txt:321692 - CCylSphere::intersects_sphere @ 0x0053b4a0 / acclient_2013_pseudo_c.txt:324573 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3361a8d776
commit
dc1e927080
3 changed files with 298 additions and 20 deletions
|
|
@ -150,15 +150,43 @@ else:
|
|||
...
|
||||
```
|
||||
|
||||
**acdream scope note:** `CylinderCollision` in `TransitionTypes.cs` does NOT implement
|
||||
this gate. That is a separate task. Cylinder-type shadow entries for ETHEREAL objects
|
||||
would not be blocked by the BSP path (BSP is separate), so the ETHEREAL Cyl gate
|
||||
primarily matters for objects that are purely Cylinder (no BSP) — which is less common
|
||||
than BSP objects. Task 3 scope covers BSP.
|
||||
**acdream implementation (Task 3 follow-up, 2026-06-24):** `CylinderCollision` in
|
||||
`TransitionTypes.cs` now implements this gate as `if (sp.ObstructionEthereal) return OK`
|
||||
at the top of the method — mirroring exactly how `SphereCollision` (consume site 1) is
|
||||
handled. The retail function is `void __thiscall`, so all returns in the ethereal branch
|
||||
are void (= OK). An ethereal Cylinder (e.g. an NPC ghost) is now fully passable.
|
||||
|
||||
---
|
||||
|
||||
## Summary: set / clear / consume flow for Task 3
|
||||
## 3b. Consume site 1 — `CSphere::intersects_sphere` (pc:321692) — full contract
|
||||
|
||||
The full decomp at pc:321692 / 0x537ae4 confirms:
|
||||
|
||||
```
|
||||
// pc:321692:
|
||||
if (obstruction_ethereal != 0 || insert_type == PLACEMENT_INSERT):
|
||||
// distSq check — if sphere NOT overlapping: return (void = passable)
|
||||
if (distSq < radiusSumSq): // overlapping:
|
||||
// calls collides_with_sphere on sphere[1] if num_sphere > 1
|
||||
// returns void — no COLLIDED
|
||||
return // void = passable in all cases
|
||||
else:
|
||||
// step_down / check_walkable / slide blocking paths
|
||||
```
|
||||
|
||||
**Key:** the `CSphere::intersects_sphere` function is `void __thiscall` — there is NO
|
||||
`return 2` (COLLIDED) from the ethereal branch. The inner `collides_with_sphere` call
|
||||
(for the penetrating case) also produces no blocking result (it may contribute to a
|
||||
side-channel contact; it does NOT stop the player). The player is passable in ALL
|
||||
sub-cases of the ethereal branch.
|
||||
|
||||
**acdream implementation (Task 3 follow-up, 2026-06-24):** `SphereCollision` implements
|
||||
`if (sp.ObstructionEthereal) return OK` at the top. The inner overlap/slide path is
|
||||
bypassed — matches the void-return semantics of the retail ethereal branch.
|
||||
|
||||
---
|
||||
|
||||
## Summary: set / clear / consume flow (complete after Task 3 follow-up)
|
||||
|
||||
```
|
||||
FindObjCollisionsLoop():
|
||||
|
|
@ -170,10 +198,10 @@ FindObjCollisionsLoop():
|
|||
// Set flag for ETHEREAL-alone
|
||||
sp.ObstructionEthereal = (obj.State & 0x4) != 0
|
||||
|
||||
// Shape dispatch (BSP path):
|
||||
// BSPQuery.FindCollisions → Path 1 checks ObstructionEthereal
|
||||
// When ObstructionEthereal=true → sphere_intersects_solid (passable)
|
||||
// When ObstructionEthereal=false → normal blocking paths
|
||||
// Shape dispatch — ALL three shapes now consume the flag:
|
||||
// BSP: BSPQuery.FindCollisions Path 1 → sphere_intersects_solid (passable)
|
||||
// Sphere: SphereCollision → if (ObstructionEthereal) return OK
|
||||
// Cylinder: CylinderCollision → if (ObstructionEthereal) return OK
|
||||
|
||||
// Clear after per-object test (retail pc:276989)
|
||||
sp.ObstructionEthereal = false
|
||||
|
|
@ -183,10 +211,12 @@ FindEnvCollisions():
|
|||
// BSP dispatch against cell walls ...
|
||||
```
|
||||
|
||||
**Contracts:**
|
||||
- ETHEREAL-only target (`state = 0x0001000C`): `ShouldSkip` returns false → flag set to
|
||||
true → BSP Path 1 fires → `sphere_intersects_solid` test → player walks through open
|
||||
door's BSP (no solid leaf at the opening) → OK → passable.
|
||||
- Non-ethereal wall: flag = false → Path 1 DOES NOT fire → normal blocking paths → player
|
||||
stopped.
|
||||
- ENV cell walls: flag cleared before BSP dispatch → ENV BSP always uses the blocking path.
|
||||
**Contracts (complete):**
|
||||
- ETHEREAL-alone BSP target (e.g. open door): `ShouldSkip` = false → flag set → BSP
|
||||
Path 1 fires → `sphere_intersects_solid` → player walks through open door. Passable.
|
||||
- ETHEREAL-alone Sphere target (e.g. ghost NPC with sphere shape): flag set →
|
||||
`SphereCollision` returns OK immediately. Passable.
|
||||
- ETHEREAL-alone Cylinder target (e.g. ghost NPC with cyl shape): flag set →
|
||||
`CylinderCollision` returns OK immediately. Passable.
|
||||
- Non-ethereal wall/object: flag = false → normal blocking paths. Blocked.
|
||||
- ENV cell walls: flag cleared before BSP dispatch → ENV BSP always blocking. Blocked.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue