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,
|
public static bool ShouldSkip(uint targetState, EntityCollisionFlags targetFlags,
|
||||||
ObjectInfoState moverState)
|
ObjectInfoState moverState)
|
||||||
{
|
{
|
||||||
// 1. Target ETHEREAL + IGNORE_COLLISIONS → walk through.
|
// 1. Target ETHEREAL → walk through.
|
||||||
// acclient_2013_pseudo_c.txt:276782 — wraps the entire body of
|
// Retail (acclient_2013_pseudo_c.txt:276782) requires BOTH
|
||||||
// FindObjCollisions; we hoist it as the first early-out.
|
// ETHEREAL_PS (0x4) AND IGNORE_COLLISIONS_PS (0x10) to wrap
|
||||||
if ((targetState & ETHEREAL_PS) != 0
|
// the entire body of FindObjCollisions and skip collision.
|
||||||
&& (targetState & IGNORE_COLLISIONS_PS) != 0)
|
// 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;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Viewer mover + creature target → walk through.
|
// 2. Viewer mover + creature target → walk through.
|
||||||
// acclient_2013_pseudo_c.txt:276787-276790.
|
// acclient_2013_pseudo_c.txt:276787-276790.
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,16 @@ public class CollisionExemptionTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void EtherealOnly_NotSkipped()
|
public void EtherealOnly_Skipped()
|
||||||
{
|
{
|
||||||
// Target with ETHEREAL but NOT IGNORE_COLLISIONS does not bail
|
// L.2g slice 1b (2026-05-13): ETHEREAL alone exempts collision.
|
||||||
// out at the first gate — collision proceeds. (Step-down marks
|
// Retail (acclient_2013_pseudo_c.txt:276782) required both bits,
|
||||||
// obstruction_ethereal, but does not exempt.)
|
// but ACE's Door.Open() broadcasts ETHEREAL alone — observed
|
||||||
Assert.False(CollisionExemption.ShouldSkip(
|
// 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,
|
targetState: ETHEREAL_PS,
|
||||||
targetFlags: EntityCollisionFlags.None,
|
targetFlags: EntityCollisionFlags.None,
|
||||||
moverState: ObjectInfoState.IsPlayer));
|
moverState: ObjectInfoState.IsPlayer));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue