diff --git a/src/AcDream.Core/Physics/BSPQuery.cs b/src/AcDream.Core/Physics/BSPQuery.cs index 6c7178b..289ff0e 100644 --- a/src/AcDream.Core/Physics/BSPQuery.cs +++ b/src/AcDream.Core/Physics/BSPQuery.cs @@ -1544,6 +1544,16 @@ public static class BSPQuery if (hit0 || hitPoly0 is not null) { + // L.2d slice 1.5 (2026-05-13): record the hit poly EARLY, + // before the StepSphereUp branch can recurse into + // ResolveWithTransition → FindObjCollisions and clobber the + // side-channel via the inner call's per-resolve clear. Path 5 + // is the dominant grounded-player path; without this the + // probe's [resolve-bldg] line for every grounded BSP hit was + // mis-labeled as "n/a (cylinder)". + if (PhysicsDiagnostics.ProbeBuildingEnabled) + PhysicsDiagnostics.LastBspHitPoly = hitPoly0; + var worldNormal = L2W(hitPoly0!.Plane.Normal); // L.2.3b (2026-04-29): recursion guard. Retail // (acclient_2013_pseudo_c.txt:272954) gates step_sphere_up on @@ -1559,9 +1569,6 @@ public static class BSPQuery // back to wall-slide so the inner sphere doesn't recurse. collisions.SetCollisionNormal(worldNormal); collisions.SetSlidingNormal(worldNormal); - // L.2d slice 1 (2026-05-13): diagnostic side-channel. - if (PhysicsDiagnostics.ProbeBuildingEnabled) - PhysicsDiagnostics.LastBspHitPoly = hitPoly0; return TransitionState.Slid; } @@ -1575,6 +1582,12 @@ public static class BSPQuery if (hit1 || hitPoly1 is not null) { + // L.2d slice 1.5 (2026-05-13): same early-record as foot + // sphere — head-sphere wall hits also recurse via + // StepSphereUp on the grounded path. + if (PhysicsDiagnostics.ProbeBuildingEnabled) + PhysicsDiagnostics.LastBspHitPoly = hitPoly1; + var worldNormal = L2W(hitPoly1!.Plane.Normal); // L.2.3b: same recursion guard as the foot-sphere branch. if (engine is not null && !path.StepUp && !path.StepDown) @@ -1582,9 +1595,6 @@ public static class BSPQuery collisions.SetCollisionNormal(worldNormal); collisions.SetSlidingNormal(worldNormal); - // L.2d slice 1 (2026-05-13): diagnostic side-channel. - if (PhysicsDiagnostics.ProbeBuildingEnabled) - PhysicsDiagnostics.LastBspHitPoly = hitPoly1; return TransitionState.Slid; } } diff --git a/src/AcDream.Core/Physics/TransitionTypes.cs b/src/AcDream.Core/Physics/TransitionTypes.cs index c881d2c..d5077b8 100644 --- a/src/AcDream.Core/Physics/TransitionTypes.cs +++ b/src/AcDream.Core/Physics/TransitionTypes.cs @@ -1469,12 +1469,14 @@ public sealed class Transition // the [resolve] probe surfaces the responsible entity id. bool collisionWasValidPre = ci.CollisionNormalValid; - // L.2d slice 1 (2026-05-13): clear the BSP-hit side-channel so the - // [resolve-bldg] emission below reads only this iteration's poly. - // Cylinder collisions leave it null on purpose (probe emits - // "hitPoly: n/a (cylinder)"). - if (PhysicsDiagnostics.ProbeBuildingEnabled) - PhysicsDiagnostics.LastBspHitPoly = null; + // L.2d slice 1.5 (2026-05-13): no per-iteration LastBspHitPoly + // clear. BSPQuery writes the side-channel early (inside + // `if (hit0 || hitPoly0 != null)` BEFORE any StepSphereUp call), + // so by the time we read it back for the [resolve-bldg] emission + // it reflects THIS entity's hit (or stays null if BSP didn't + // hit). For cylinder dispatch we key the "n/a (cylinder)" label + // off `obj.CollisionType` directly at the emission site, so a + // stale BSP value from a prior iteration can't leak through. TransitionState result; @@ -1588,10 +1590,19 @@ public sealed class Transition $" entOrigin_lb=({entOriginLb.X:F1},{entOriginLb.Y:F1},{entOriginLb.Z:F1})")); var poly = PhysicsDiagnostics.LastBspHitPoly; - if (poly is null) + // L.2d slice 1.5 (2026-05-13): key the n/a label on the + // entity's CollisionType, not on LastBspHitPoly nullness — + // a BSP hit with null side-channel indicates a BSPQuery code + // path that didn't write (a bug; we should fix it, not + // pretend the entity was a cylinder). + if (obj.CollisionType == ShadowCollisionType.Cylinder) { sb.Append("\n hitPoly: n/a (cylinder)"); } + else if (poly is null) + { + sb.Append("\n hitPoly: n/a (BSP path — side-channel not written, missing BSPQuery wire site)"); + } else { sb.Append(System.FormattableString.Invariant(