fix(phys L.2g slice 1b): widen CollisionExemption to ETHEREAL alone
B.4b visual test confirmed the L.2g slice 1 handoff's open question: ACE's Door.Open() broadcasts state=0x0001000C (HasPhysicsBSP | Ethereal | ReportCollisions), NOT the state=0x14+ that retail servers send (Ethereal | IgnoreCollisions). The L.2g pipeline correctly mutates ShadowObjectRegistry with the new state, but CollisionExemption.ShouldSkip required both bits and the door stayed solid. Retail (acclient_2013_pseudo_c.txt:276782) wraps FindObjCollisions in `if NOT (state & ETHEREAL && state & IGNORE_COLLISIONS)`. ETHEREAL alone takes a different retail path at line 276795 that sets sphere_path.obstruction_ethereal = 1 and lets downstream movement allow passage despite the contact. We haven't ported that downstream path yet. Pragmatic shortcut: widen the early-out to ETHEREAL alone so doors become passable when ACE flips the bit. Retail-server broadcasts still hit the same branch correctly (both bits set implies ETHEREAL). Compatible with both server styles. Renames test EtherealOnly_NotSkipped -> EtherealOnly_Skipped and flips its assertion. 13 CollisionExemption tests pass; full suite 1046 pass / 8 pre-existing baseline fail (unchanged). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
58b95bc0c5
commit
a6e4b5709f
2 changed files with 26 additions and 12 deletions
|
|
@ -59,14 +59,24 @@ public static class CollisionExemption
|
|||
public static bool ShouldSkip(uint targetState, EntityCollisionFlags targetFlags,
|
||||
ObjectInfoState moverState)
|
||||
{
|
||||
// 1. Target ETHEREAL + IGNORE_COLLISIONS → walk through.
|
||||
// acclient_2013_pseudo_c.txt:276782 — wraps the entire body of
|
||||
// FindObjCollisions; we hoist it as the first early-out.
|
||||
if ((targetState & ETHEREAL_PS) != 0
|
||||
&& (targetState & IGNORE_COLLISIONS_PS) != 0)
|
||||
{
|
||||
// 1. Target ETHEREAL → walk through.
|
||||
// Retail (acclient_2013_pseudo_c.txt:276782) requires BOTH
|
||||
// ETHEREAL_PS (0x4) AND IGNORE_COLLISIONS_PS (0x10) to wrap
|
||||
// the entire body of FindObjCollisions and skip collision.
|
||||
// ETHEREAL alone takes a different retail path (line 276795
|
||||
// sets sphere_path.obstruction_ethereal = 1 and downstream
|
||||
// movement allows passage despite the contact). We haven't
|
||||
// ported that downstream path yet.
|
||||
//
|
||||
// L.2g slice 1b (2026-05-13): ACE's Door.Open() sends only
|
||||
// ETHEREAL (state=0x0001000C observed live), not the
|
||||
// ETHEREAL|IGNORE_COLLISIONS combo retail servers broadcast.
|
||||
// Pragmatic shortcut: exempt on ETHEREAL alone so doors
|
||||
// become passable when ACE flips the bit. Retail-server
|
||||
// broadcasts (state=0x14+) still hit this branch correctly
|
||||
// because both bits set implies ETHEREAL set.
|
||||
if ((targetState & ETHEREAL_PS) != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. Viewer mover + creature target → walk through.
|
||||
// acclient_2013_pseudo_c.txt:276787-276790.
|
||||
|
|
|
|||
|
|
@ -42,12 +42,16 @@ public class CollisionExemptionTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void EtherealOnly_NotSkipped()
|
||||
public void EtherealOnly_Skipped()
|
||||
{
|
||||
// Target with ETHEREAL but NOT IGNORE_COLLISIONS does not bail
|
||||
// out at the first gate — collision proceeds. (Step-down marks
|
||||
// obstruction_ethereal, but does not exempt.)
|
||||
Assert.False(CollisionExemption.ShouldSkip(
|
||||
// L.2g slice 1b (2026-05-13): ETHEREAL alone exempts collision.
|
||||
// Retail (acclient_2013_pseudo_c.txt:276782) required both bits,
|
||||
// but ACE's Door.Open() broadcasts ETHEREAL alone — observed
|
||||
// live: state=0x0001000C (HasPhysicsBSP | Ethereal | ReportCollisions).
|
||||
// Pragmatic shortcut: widen the early-out to ETHEREAL alone so
|
||||
// doors become passable when ACE flips the bit. Retail-server
|
||||
// broadcasts (state=0x14+) still hit the same branch correctly.
|
||||
Assert.True(CollisionExemption.ShouldSkip(
|
||||
targetState: ETHEREAL_PS,
|
||||
targetFlags: EntityCollisionFlags.None,
|
||||
moverState: ObjectInfoState.IsPlayer));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue