diff --git a/src/AcDream.Core/Physics/TransitionTypes.cs b/src/AcDream.Core/Physics/TransitionTypes.cs index 18bab97..c941f67 100644 --- a/src/AcDream.Core/Physics/TransitionTypes.cs +++ b/src/AcDream.Core/Physics/TransitionTypes.cs @@ -1439,6 +1439,38 @@ public sealed class Transition // Use the full 6-path BSP dispatcher for retail-faithful collision. // Use pre-resolved polygons (vertices+planes computed at cache time). + // + // 2026-05-20 (Bug B fix): pass the cell's world rotation + + // translation so the BSP's internal find_walkable / + // step_sphere_down / Path-4 land write world-space ContactPlanes + // (NOT cell-local). The prior call passed Quaternion.Identity + // and defaulted worldOrigin = Vector3.Zero — which corrupted + // every Path 3 + Path 4 CP write inside an indoor cell with + // D ≈ 0 instead of D = -world_floor_Z. Mirrors the existing + // correct pattern at the FindObjCollisions call site (~line + // 1808: passes obj.Rotation + worldOrigin: obj.Position). + // + // Retail oracle: BSPTREE::find_collisions (decomp + // acclient_2013_pseudo_c.txt:323924) calls Plane::localtoglobal + // (:323921) before set_contact_plane. Our TransformNormal + + // TransformVertices + BuildWorldPlane chain is the equivalent + // — it just needs the right rotation + origin. + Quaternion cellRotation; + Vector3 cellOrigin; + if (!Matrix4x4.Decompose(cellPhysics.WorldTransform, out _, out cellRotation, out cellOrigin)) + { + // Matrix has shear or other non-decomposable parts. EnvCell + // WorldTransform is always rigid rotation + translation per + // the dat format, so this branch should never fire. Log a + // warning + fall back to identity rotation + .Translation + // so we degrade to "translation-only" instead of the prior + // "both broken". + Console.WriteLine(System.FormattableString.Invariant( + $"[indoor-bsp] WARN cellPhysics.WorldTransform did not decompose cleanly for cell 0x{sp.CheckCellId:X8} — falling back to identity rotation")); + cellRotation = Quaternion.Identity; + cellOrigin = cellPhysics.WorldTransform.Translation; + } + var cellState = BSPQuery.FindCollisions( cellPhysics.BSP.Root, cellPhysics.Resolved, @@ -1448,8 +1480,9 @@ public sealed class Transition localCurrCenter, Vector3.UnitZ, // local space Z is up 1.0f, // scale = 1.0 for cell geometry - Quaternion.Identity, - engine); // engine needed for Path 5 step-up + cellRotation, + engine, // engine needed for Path 5 step-up + worldOrigin: cellOrigin); if (PhysicsDiagnostics.ProbeIndoorBspEnabled) {