From 7ac8f544a7ed57d5dd2afc45701437b1fb85dbdc Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 21 May 2026 09:12:30 +0200 Subject: [PATCH] =?UTF-8?q?fix(physics):=20#89=20=E2=80=94=20sphere-overla?= =?UTF-8?q?p=20in=20CheckBuildingTransit=20closes=20login-inside-inn=20cla?= =?UTF-8?q?ssification=20race?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Outdoor→indoor entry path used PointInsideCellBsp (point-only) for the building-portal containment test. When the player logs in INSIDE a building and the foot-sphere center is just past the destination cell's CellBSP boundary, the point-only check failed → CellId stuck as outdoor → indoor BSP queries never ran → walls passable. User-reported symptom: "logged in in the inn, at start ran through exterior walls, ran back in and they block now." Fix: swap PointInsideCellBsp for SphereIntersectsCellBsp (the radius- aware port from #90). Promotes CellId to the interior cell the moment ANY part of the foot-sphere crosses the destination cell boundary — matches retail's CCellStruct::sphere_intersects_cell timing at acclient_2013_pseudo_c.txt:317666 exactly. The sphereRadius parameter was already plumbed through CheckBuildingTransit per #89's documented "future upgrade" note from 2026-05-19 (which is exactly today's symptom). Closes #89. 1147 + 8 baseline maintained. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.Core/Physics/CellTransit.cs | 41 +++++++++++++------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/AcDream.Core/Physics/CellTransit.cs b/src/AcDream.Core/Physics/CellTransit.cs index bd62333..c7d9cf0 100644 --- a/src/AcDream.Core/Physics/CellTransit.cs +++ b/src/AcDream.Core/Physics/CellTransit.cs @@ -155,25 +155,19 @@ public static class CellTransit /// BuildingObj::find_building_transit_cells + /// EnvCell::check_building_transit. For each portal of the /// outdoor building, look up the destination interior cell and test - /// whether the sphere center is inside it via - /// . If so, add the interior - /// cell to . + /// whether the sphere overlaps it via + /// . If so, add the + /// interior cell to . /// /// - /// Retail divergence: retail's check_building_transit - /// uses CCellStruct::sphere_intersects_cell (radius-aware - /// BSP-vs-sphere test) which fires the moment ANY part of the sphere - /// overlaps the destination cell. Our port uses - /// (radius-less, tests only - /// the sphere CENTER). Practical effect: entry into a building fires - /// when the player's foot-sphere center crosses the destination cell - /// boundary — roughly (~0.48m) DEEPER - /// into the doorway than retail. If visual verification at the cottage - /// door shows a noticeable "late entry" effect (player visually inside - /// the building before walls switch from outdoor-stab to indoor-cell), - /// port sphere_intersects_cell in a follow-up. - /// is plumbed through for that future - /// upgrade; currently unused. + /// Issue #89 closed (2026-05-20): uses retail's radius-aware + /// CCellStruct::sphere_intersects_cell + /// (acclient_2013_pseudo_c.txt:317666) ported as + /// . Promotes CellId to + /// the interior cell the moment ANY part of the foot-sphere crosses + /// the cell boundary — matches retail entry timing exactly and + /// closes the login-inside-inn classification race where the player + /// would briefly be classified outdoor and walk through walls. /// /// public static void CheckBuildingTransit( @@ -198,13 +192,22 @@ public static class CellTransit } // Sphere center in the OTHER cell's local space. + // Issue #89 closed (2026-05-20): use radius-aware sphere-overlap + // (matches retail's CCellStruct::sphere_intersects_cell at + // acclient_2013_pseudo_c.txt:317666) instead of point-only. This + // promotes the player's CellId to the interior cell the moment + // ANY part of the foot-sphere crosses the cell boundary — the + // entry-side counterpart to issue #90's sticky-stay fix. Without + // it, login-inside-the-inn keeps the player classified outdoor + // until they walk further in (sphere center crosses), letting + // them run through exterior walls on the way out. var localCenter = Vector3.Transform(worldSphereCenter, otherCell.InverseWorldTransform); - bool inside = BSPQuery.PointInsideCellBsp(otherCell.CellBSP.Root, localCenter); + bool inside = BSPQuery.SphereIntersectsCellBsp(otherCell.CellBSP.Root, localCenter, sphereRadius); if (PhysicsDiagnostics.ProbeIndoorBspEnabled) { Console.WriteLine(System.FormattableString.Invariant( - $"[check-bldg] portal->0x{portal.OtherCellId:X8} wpos=({worldSphereCenter.X:F3},{worldSphereCenter.Y:F3},{worldSphereCenter.Z:F3}) lpos=({localCenter.X:F3},{localCenter.Y:F3},{localCenter.Z:F3}) inside={inside}")); + $"[check-bldg] portal->0x{portal.OtherCellId:X8} wpos=({worldSphereCenter.X:F3},{worldSphereCenter.Y:F3},{worldSphereCenter.Z:F3}) lpos=({localCenter.X:F3},{localCenter.Y:F3},{localCenter.Z:F3}) r={sphereRadius:F3} inside={inside}")); } if (inside)