namespace AcDream.Core.Physics; /// /// The retail-faithful exemption gate at the top of /// CPhysicsObj::FindObjCollisions. Decides — based on the moving /// object's bits and the target's raw /// PhysicsState + decoded — /// whether collision against the target should be skipped entirely /// (return OK_TS) or proceed to broad-phase / shape dispatch. /// /// /// Ported from the named retail decompilation: /// /// /// acclient_2013_pseudo_c.txt:276782 — target /// ETHEREAL_PS=0x4 & IGNORE_COLLISIONS_PS=0x10: walk through. /// acclient_2013_pseudo_c.txt:276787 — viewer mover vs /// creature target: walk through (camera ray ignores creatures). /// acclient_2013_pseudo_c.txt:276971 — mover with /// IGNORE_CREATURES (state & 0x400) vs creature target: /// walk through. /// acclient_2013_pseudo_c.txt:276807-276839 — PvP rule: /// /// If both are players: skip unless target is Impenetrable, /// or both are PK, or both are PKLite. Mismatched PK status (PK vs /// non-PK, PK vs PKLite) is exempted — players in different pools /// pass through each other, matching retail muscle memory. /// /// /// /// /// /// Cross-checked against ACE /// references/ACE/Source/ACE.Server/Physics/PhysicsObj.cs:381-405 /// (line-for-line C# port of the same logic). Note: ACE adds /// state.HasFlag(IsImpenetrable) (mover-impenetrable) to the /// collide list; retail's pseudo-C only checks the target's /// IsImpenetrable(). acdream follows retail. /// /// public static class CollisionExemption { private const uint ETHEREAL_PS = 0x4u; // acclient.h:2819 private const uint IGNORE_COLLISIONS_PS = 0x10u; // acclient.h:2821 /// /// Should the moving object skip collision testing against this /// target entirely? Returns true if exempt (no further /// shape dispatch). /// /// Raw retail PhysicsState bits /// captured at CreateObject time (ETHEREAL/IGNORE/etc.). /// Decoded /// from the target's PWD bitfield /// plus its ItemType-derived IsCreature bit. /// The moving object's /// — typically the local player's /// IsPlayer + (PK/PKLite/Impenetrable bits if known) flags. public static bool ShouldSkip(uint targetState, EntityCollisionFlags targetFlags, ObjectInfoState moverState) { // 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. bool moverIsViewer = (moverState & ObjectInfoState.IsViewer) != 0; bool targetIsCreature = (targetFlags & EntityCollisionFlags.IsCreature) != 0; if (moverIsViewer && targetIsCreature) return true; // 3. IGNORE_CREATURES mover + creature target → walk through. // acclient_2013_pseudo_c.txt:276971. bool moverIgnoresCreatures = (moverState & ObjectInfoState.IgnoreCreatures) != 0; if (moverIgnoresCreatures && targetIsCreature) return true; // 4. PvP exemption block. // acclient_2013_pseudo_c.txt:276807-276839. bool moverIsPlayer = (moverState & ObjectInfoState.IsPlayer) != 0; bool targetIsPlayer = (targetFlags & EntityCollisionFlags.IsPlayer) != 0; if (moverIsPlayer && targetIsPlayer) { // Tentatively exempt (retail `ebp_1 = 1`). Then disqualify // if any of the COLLIDE conditions hold. bool collide = false; // 4a. Impenetrable target → collide. // acclient_2013_pseudo_c.txt:276826. if ((targetFlags & EntityCollisionFlags.IsImpenetrable) != 0) collide = true; // 4b. Both PK → collide. // acclient_2013_pseudo_c.txt:276832-276836. if (!collide && (moverState & ObjectInfoState.IsPK) != 0 && (targetFlags & EntityCollisionFlags.IsPK) != 0) { collide = true; } // 4c. Both PKLite → collide. // acclient_2013_pseudo_c.txt:276837. if (!collide && (moverState & ObjectInfoState.IsPKLite) != 0 && (targetFlags & EntityCollisionFlags.IsPKLite) != 0) { collide = true; } if (!collide) return true; // exempt — non-PK pair walks through } return false; // proceed to broad-phase + shape dispatch } }