fix(physics): #150 — skip ethereal targets in the step-down pass (open doors fully passable)

An OPEN door (ETHEREAL_PS 0x4 set on Use) still stopped the player with a
residual threshold block after the collision sweep, despite swinging open
visually.

The resolver runs two collision passes per step: the main sweep and a
step-down (foot-sphere "is there floor?") sub-pass. acdream tested the
ethereal door in BOTH; the main pass cleared it (BSPQuery Path 1 + the
Layer-2 override) but the step-down pass had no escape, leaving a Collided
result at the sill -- the "can-sized cylinder on the ground threshold".

Retail's CPhysicsObj::FindObjCollisions (pc:276795-276806) SKIPS an ethereal
target when sphere_path.step_down != 0 -- it only tests it in the main pass.
So an open door is fully passable everywhere; the swung panel's position is
irrelevant (ethereal = no collision; the swing animation is purely visual).

Port that branch verbatim: an ethereal-for-this-test target (target state &
0x4, OR mover-ethereal vs a non-static target) is `continue`d when
sp.StepDown is set.

Live-verified (user confirmed): the door now blocks 0x while open (0x1000C),
still blocks while closed (0x10008); pre-fix it blocked 217x while open.
Core suite green (1595/0).

(An earlier "animate the door collision" theory was wrong and dropped -- if
collision tracked the swung panel you'd bump the panel in its open position,
which retail does not do. The user caught it.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-25 12:20:54 +02:00
parent 2ac7ea776f
commit aa7c0b5c31
2 changed files with 47 additions and 9 deletions

View file

@ -5482,6 +5482,27 @@ outdoors at the angle that previously erased it.
# Recently closed
## #150 — Open doors still blocked at the threshold (ethereal target not skipped in the step-down pass)
**Status:** DONE (2026-06-25) — after the retail collision sweep, an OPEN door
(ETHEREAL_PS 0x4 set on Use) still stopped the player with a small residual block at
the sill ("can-sized cylinder on the ground threshold"), even though the door swings
open visually. Root: the resolver runs two collision passes per step — the main
sweep and a step-down (foot-sphere "is there floor?") sub-pass. acdream tested the
ethereal door in BOTH; the main pass cleared it (Layer-1 Path-1 + the Layer-2
override) but the **step-down pass had no escape** and its `Collided` result survived
as the threshold block. Retail's `CPhysicsObj::FindObjCollisions` (pc:276795-276806)
SKIPS an ethereal target entirely when `sphere_path.step_down != 0` — it only tests
it in the main pass — so an open door is fully passable everywhere (the swung panel's
position is irrelevant; ethereal = no collision, the animation is purely visual).
Fix: port that exact branch — `if ((state & 0x4) || (mover.Ethereal && (state & 0x1)
== 0)) && sp.StepDown → continue`. `TransitionTypes.cs` per-object loop. Live-verified
(user "Yes it works"; the door now blocks 0× while open [0x1000C], still blocks while
closed [0x10008]; pre-fix it blocked 217× while open). Core suite green (1595/0). The
earlier "animate the door collision" theory was WRONG and dropped — the user correctly
noted that if collision followed the swung panel you'd bump the panel in its open
position, which retail does not do. Likely also advances the door half of #137.
## #149 — BSP-less landblock statics (torches/braziers/lamp-posts) were walk-through
**Status:** DONE (2026-06-25, `4cf6eeb`) — town props placed as landblock stabs