fix(phys): A6.P7 — retail-binary cyl-vs-BSP dispatch (HAS_PHYSICS_BSP_PS gate)
Closes the door-cyl phantom slide where a sphere approaching a closed cottage door at NE/SE headings could be blocked by the cyl's radial normal contaminating the slide tangent into the slab face (live evidence in door-a6p6-v2.utf8.log: 12 resolves with cn=(0.86,0.51,0) attributed to door entity 0x000F4245). Retail anchor: CPhysicsObj::FindObjCollisions at acclient_2013_pseudo_c.txt:276861 dispatches BINARILY between BSP-only and cyl+sphere based on HAS_PHYSICS_BSP_PS (0x10000 in acclient.h:2833). For non-PvP, non-missile movers — every M1.5 scope walking-vs-static scenario — an entity with the flag set tests its BSP exclusively; the foot cyl is never tested. ACE confirms the truth table at PhysicsObj.cs:412-450 (HasPhysicsBSP, missileIgnore, exemption). Our dispatcher iterated every ShadowEntry independently and tested both the cyl AND the BSP for a closed door. Cyl was registered first (FromSetup walk order), and its diagonal radial slide normal "won" attribution at the early-return on first non-OK. Result was out=in for tangential motion along the door face. Changes (~15 LOC + 7 unit tests): - PhysicsStateFlags.HasPhysicsBsp = 0x00010000 (PhysicsBody.cs) - Transition.BspOnlyDispatch(uint state) static predicate (TransitionTypes.cs) — mirrors retail's branch with M1.5 scope defaults (ebp_1 and eax_12 treated as false; wire PvP / missile refinements when those scopes ship) - Per-entry guard in FindObjCollisions cyl/sphere branch (TransitionTypes.cs:2433) — continue when BspOnlyDispatch fires, with [cyl-skip-bsp] diagnostic line gated on ProbeBuildingEnabled - A6P7DispatchRulesTests (7 tests, all GREEN): flag value + 6 parameterized predicate cases Verification: 14-test keep-green list from the 2026-05-25 handoff passes (5 BSPQueryTests.FindCollisions_Path5_*, 2 CellTransitTests.A6P5_*, 2 DoorCollisionApparatusTests.Apparatus_DeadCenter_*, 5 DoorBugTrajectoryReplayTests, 1 CellarUpTrajectoryReplayTests.LiveCompare_FirstCap_FixClosesCottageFloorCap). Total: 20/20 pass including the new 7-test predicate suite. The DocumentsBug test (Apparatus_Grounded_50cmOffCenter) fails post-fix BUT was already failing pre-fix in the worktree baseline (verified by stashing the fix and re-running — same failure mode: sphere blocks at start with floor normal (0,0,1)). Not in the keep-green list, so this is a known pre-existing condition; the test's own header comment instructs flipping the assertion when the fix lands. Investigation: docs/research/2026-05-25-a6-door-cyl-retail-dispatch-investigation.md Needs visual verification at Holtburg cottage door (NE/SE approach should now slide smoothly along the door face — zero [cyl-test] log lines attributed to door entity, replaced by [cyl-skip-bsp]). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b36eff1c10
commit
888272aad1
4 changed files with 492 additions and 0 deletions
|
|
@ -32,6 +32,19 @@ public enum PhysicsStateFlags : uint
|
|||
Gravity = 0x00000400, // bit 10 — apply downward gravity
|
||||
Hidden = 0x00001000,
|
||||
/// <summary>
|
||||
/// A6.P7 (2026-05-25): retail HAS_PHYSICS_BSP_PS bit
|
||||
/// (acclient.h:2833). When set, the entity exposes a per-Setup
|
||||
/// BSP collision mesh; retail's
|
||||
/// <c>CPhysicsObj::FindObjCollisions</c> at
|
||||
/// acclient_2013_pseudo_c.txt:276861 dispatches the entity's
|
||||
/// collision queries to the BSP path EXCLUSIVELY for non-PvP,
|
||||
/// non-missile movers — the foot cylinder and per-Setup spheres
|
||||
/// are NEVER tested in this case. Closed cottage doors have
|
||||
/// state 0x10008 (STATIC | REPORT_COLLISIONS | HAS_PHYSICS_BSP).
|
||||
/// ACE name: <c>PhysicsState.HasPhysicsBSP</c>.
|
||||
/// </summary>
|
||||
HasPhysicsBsp = 0x00010000, // bit 16 — retail HAS_PHYSICS_BSP_PS
|
||||
/// <summary>
|
||||
/// L.3a (2026-04-30): retail INELASTIC_PS bit (acclient.h:2834).
|
||||
/// When set, wall-collisions zero the velocity instead of reflecting.
|
||||
/// Used by spell projectiles and missiles that should embed/explode on
|
||||
|
|
|
|||
|
|
@ -586,6 +586,58 @@ public sealed class Transition
|
|||
private static bool DumpEdgeSlideEnabled =>
|
||||
Environment.GetEnvironmentVariable("ACDREAM_DUMP_EDGE_SLIDE") == "1";
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// A6.P7 (2026-05-25) — retail-binary dispatch rule
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// A6.P7 retail-binary dispatch predicate. Returns true when an
|
||||
/// entity's collision queries should go to its BSP exclusively,
|
||||
/// skipping the cyl/sphere shapes.
|
||||
///
|
||||
/// <para>
|
||||
/// Mirrors the dispatch branch in retail's
|
||||
/// <c>CPhysicsObj::FindObjCollisions</c> at
|
||||
/// <c>docs/research/named-retail/acclient_2013_pseudo_c.txt:276861</c>:
|
||||
/// <code>
|
||||
/// if (((state & 0x10000) == 0 || ebp_1 != 0) || eax_12 != 0)
|
||||
/// // cyl + sphere iteration
|
||||
/// else
|
||||
/// // BSP-only via CPartArray::FindObjCollisions
|
||||
/// </code>
|
||||
/// where <c>ebp_1</c> is the PvP-target-player flag (lines 276808-
|
||||
/// 276841) and <c>eax_12</c> is the <c>OBJECTINFO::missile_ignore</c>
|
||||
/// result (line 274385). The flag is named
|
||||
/// <c>HAS_PHYSICS_BSP_PS = 0x10000</c> in acclient.h:2833 and
|
||||
/// <c>PhysicsState.HasPhysicsBSP</c> in ACE
|
||||
/// (references/ACE/Source/ACE.Entity/Enum/PhysicsState.cs:24).
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// M1.5 scope (walking-vs-static, no PK, no missiles) treats both
|
||||
/// <c>ebp_1</c> and <c>eax_12</c> as <c>false</c>. The predicate
|
||||
/// reduces to <c>(state & HAS_PHYSICS_BSP_PS) != 0</c>. When
|
||||
/// PK ships (M2+ phase) and missiles ship (F.3), wire the
|
||||
/// PvP-exemption and missile_ignore checks through as additional
|
||||
/// parameters following retail's
|
||||
/// <c>references/ACE/Source/ACE.Server/Physics/PhysicsObj.cs:412</c>
|
||||
/// dispatch shape.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Investigation:
|
||||
/// <c>docs/research/2026-05-25-a6-door-cyl-retail-dispatch-investigation.md</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="entityState">The collision target entity's raw
|
||||
/// <c>PhysicsState</c> value (as stored on
|
||||
/// <c>ShadowEntry.State</c>).</param>
|
||||
/// <returns>True when retail would dispatch BSP-only — i.e. when
|
||||
/// the entity has <c>HAS_PHYSICS_BSP_PS</c> set; cyl/sphere shapes
|
||||
/// must be skipped at the per-entry dispatch site.</returns>
|
||||
public static bool BspOnlyDispatch(uint entityState)
|
||||
=> (entityState & (uint)PhysicsStateFlags.HasPhysicsBsp) != 0;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Public entry point
|
||||
// -----------------------------------------------------------------------
|
||||
|
|
@ -2383,6 +2435,36 @@ public sealed class Transition
|
|||
// ACE: Sphere.IntersectsSphere handles CylSphere objects via
|
||||
// the same 6-path dispatcher. For now we keep the swept-sphere
|
||||
// cylinder test which matches the retail CylSphere behavior.
|
||||
|
||||
// A6.P7 (2026-05-25) — retail-binary dispatch. Retail's
|
||||
// CPhysicsObj::FindObjCollisions at
|
||||
// acclient_2013_pseudo_c.txt:276861 dispatches BSP-only
|
||||
// when HAS_PHYSICS_BSP_PS (0x10000) is set on the entity
|
||||
// (and the mover isn't a PvP-eligible player or
|
||||
// missile-ignored). Cottage doors carry the flag in their
|
||||
// state (0x10008), so retail tests their slab BSP
|
||||
// exclusively — the foot cyl is never tested. Without
|
||||
// this guard, our dispatcher iterated cyl FIRST (it's
|
||||
// registered before the BSP shape) and its radial normal
|
||||
// contaminated the slide direction at NE/SE approach
|
||||
// headings, producing the "stuck on door" phantom in
|
||||
// door-a6p6-v2.utf8.log. See investigation at
|
||||
// docs/research/2026-05-25-a6-door-cyl-retail-dispatch-investigation.md.
|
||||
//
|
||||
// M1.5 scope: PvP exemption (ebp_1) and missile_ignore
|
||||
// (eax_12) are treated as false. Wire them through when
|
||||
// PK / missiles ship — matches retail's full predicate
|
||||
// at line 276861.
|
||||
if (BspOnlyDispatch(obj.State))
|
||||
{
|
||||
if (PhysicsDiagnostics.ProbeBuildingEnabled)
|
||||
{
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[cyl-skip-bsp] obj=0x{obj.EntityId:X8} state=0x{obj.State:X8} — HAS_PHYSICS_BSP_PS dispatches BSP-only"));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
result = CylinderCollision(obj, sp, engine);
|
||||
|
||||
// A6.P4 door investigation (2026-05-24): log every Cylinder
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue