fix(physics): port retail Layer-2 ethereal override -- open doors passable (pc:276961)
CPhysicsObj::FindObjCollisions at pc:276961-276989 has a two-layer mechanism for
ethereal doors. Layer 1 (BSPQuery Path-1 sphere_intersects_solid, pc:323742) already
ported in Task 3 handles the open-gap case. Layer 2 (pc:276963-276977) is a
force-reset that catches the residual case: when the player's sphere CENTER crosses
a thin ethereal slab, sphere_intersects_solid can still return Collided via
HitsSphere (polygon contact on the slab face). Without Layer 2, the opened door
remains an invisible wall until the player's center passes ~0.48m beyond the slab.
Port: in FindObjCollisionsInCell, immediately after the shape dispatch (BSP / Sphere /
Cylinder branches), insert the Layer-2 gate:
if (result != OK && sp.ObstructionEthereal && !sp.StepDown && (obj.State & 0x1u) == 0)
{ result = OK; ci.CollisionNormalValid = false; }
The STATIC_PS (0x1) guard (pc:276969) ensures static-ethereal env geometry still
blocks. Cylinder/Sphere shapes already return OK from their own ObstructionEthereal
early-outs so Layer 2 is effectively BSP-only in practice, but is written
unconditionally matching retail.
Tests (ObstructionEtherealTests): three new BSP Layer-2 cases using a synthetic
wall polygon in local-space coordinates. (A) ethereal non-static: passable. (B)
ethereal+static: still blocks. (C) non-ethereal: still blocks. All 11 tests pass;
DoorCollision/CellarUp/CornerFlood/HouseExitWalk regression gate: 25/25 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6c7e3ef1ab
commit
1a7c5aa006
2 changed files with 248 additions and 34 deletions
|
|
@ -2775,6 +2775,37 @@ public sealed class Transition
|
|||
}
|
||||
}
|
||||
|
||||
// Layer-2 ethereal override — retail CPhysicsObj::FindObjCollisions
|
||||
// pc:276961-276989 (0x0050f1ac-0x0050f31e). After the shape dispatch
|
||||
// (Layer 1), retail force-resets the result to OK for a NON-STATIC
|
||||
// ethereal target when step_down is NOT set. Layer 1 (BSPQuery Path-1
|
||||
// sphere_intersects_solid) handles the no-solid-leaf case (open door
|
||||
// gap) already, but a thin ethereal slab (closed-but-ethereal / the
|
||||
// player's sphere CENTER crosses the slab) can still return Collided.
|
||||
// Layer 2 suppresses that so the door is FULLY passable once ethereal.
|
||||
//
|
||||
// Gate conditions (pc:276963-276973):
|
||||
// 276963: state_3 != OK_TS ← result is a block
|
||||
// 276967: sphere_path.step_down == 0 ← not a step-down query
|
||||
// 276969: (target->state & 1) == 0 ← NOT STATIC_PS (0x1)
|
||||
// 276971: var_c != 0 || ... ← obstruction_ethereal set
|
||||
// (the || ... is the IGNORE_CREATURES+creature case — handled
|
||||
// upstream in CollisionExemption; Layer 2 only needs the var_c term)
|
||||
// Effect (pc:276973-276977):
|
||||
// state_3 = OK_TS ← force passable
|
||||
// collision_normal_valid = 0 ← clear stale slide normal
|
||||
// Note: Cylinder and Sphere shapes already return OK from their own
|
||||
// obstruction_ethereal early-out, so this clause only fires in practice
|
||||
// for the BSP branch, but is written unconditionally as retail does.
|
||||
if (result != TransitionState.OK
|
||||
&& sp.ObstructionEthereal
|
||||
&& !sp.StepDown
|
||||
&& (obj.State & 0x1u) == 0) // STATIC_PS=0x1 (acclient.h:2815): static ethereal keeps Layer-1 only
|
||||
{
|
||||
result = TransitionState.OK;
|
||||
ci.CollisionNormalValid = false; // collision_normal_valid = 0 (pc:276975)
|
||||
}
|
||||
|
||||
// L.2a slice 3: attribute the collision (if any) to this entity.
|
||||
// Two cases:
|
||||
// - result != OK: the object stopped the transition (hard-block).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue