From c0d84057cb85626d546505aa403e60a302915ce3 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 21 May 2026 09:03:51 +0200 Subject: [PATCH] =?UTF-8?q?fix(physics):=20#91=20=E2=80=94=20query=20indoo?= =?UTF-8?q?r=20cell=20shadows=20in=20FindObjCollisions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Interior items (fireplaces, tables, chests) registered via A1.5's ShadowObjectRegistry.Register `cellScope` parameter (commit 4d3bf6f) are stored under their ParentCellId key (e.g. 0xA9B40121). But GetNearbyObjects's broad-phase only iterates outdoor 24m landcell keys (0xA9B40029 etc) and never looks up indoor cell keys, so interior shadows were registered but unreachable. User-visible symptom: tables/boxes/fireplaces don't block movement, while walls DO block (the indoor BSP path is separate). Fix: GetNearbyObjects accepts an optional indoorCellIds parameter and additionally queries _cells[indoorCellId] for each entry with low-byte >= 0x0100u. FindObjCollisions computes the set via CellTransit.FindCellSet (same set A4 uses for multi-cell BSP iteration) and passes it through. Outdoor seeds typically produce sets containing only outdoor land-cells which the new branch filters out, so the outdoor-only behavior is preserved. 1147 + 8 baseline maintained. Closes the user-reported regression "walls block now correct but interior items such as tables and boxes or fireplaces do not block." Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Physics/ShadowObjectRegistry.cs | 31 ++++++++++++++++++- src/AcDream.Core/Physics/TransitionTypes.cs | 21 ++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/AcDream.Core/Physics/ShadowObjectRegistry.cs b/src/AcDream.Core/Physics/ShadowObjectRegistry.cs index 9c52435..b574591 100644 --- a/src/AcDream.Core/Physics/ShadowObjectRegistry.cs +++ b/src/AcDream.Core/Physics/ShadowObjectRegistry.cs @@ -242,14 +242,43 @@ public sealed class ShadowObjectRegistry /// Get all objects near a world position. Searches the given landblock plus /// all 8 adjacent landblocks to handle objects near cell/landblock boundaries. /// Within each landblock, queries only the cells the query sphere overlaps. + /// + /// + /// Issue #91 (2026-05-20): the optional + /// parameter is the candidate set of indoor cells the foot-sphere overlaps + /// (from ). When supplied, indoor + /// shadows registered via 's cellScope + /// parameter (A1.5 fix at `4d3bf6f`) are ALSO included in the result. + /// Without this, interior statics (fireplaces, tables, chests) registered + /// against e.g. `0xA9B40121` are stored under that key but the outdoor- + /// grid lookup (cell ids like `0xA9B40029`) never queries the indoor key. + /// Net effect pre-fix: interior items don't block movement. + /// /// public void GetNearbyObjects(Vector3 worldPos, float queryRadius, float worldOffsetX, float worldOffsetY, uint landblockId, - List results) + List results, + System.Collections.Generic.IReadOnlyCollection? indoorCellIds = null) { results.Clear(); var seen = new HashSet(); + // Indoor-scoped shadows (A1.5 cellScope). Query first so the + // outdoor-grid lookup below skips duplicates via `seen`. + if (indoorCellIds is not null) + { + foreach (uint indoorCellId in indoorCellIds) + { + if ((indoorCellId & 0xFFFFu) < 0x0100u) continue; // skip outdoor ids + if (!_cells.TryGetValue(indoorCellId, out var list)) continue; + foreach (var entry in list) + { + if (seen.Add(entry.EntityId)) + results.Add(entry); + } + } + } + // Extract landblock X/Y from the ID. int lbX = (int)((landblockId >> 24) & 0xFF); int lbY = (int)((landblockId >> 16) & 0xFF); diff --git a/src/AcDream.Core/Physics/TransitionTypes.cs b/src/AcDream.Core/Physics/TransitionTypes.cs index e5f1e86..dceaab8 100644 --- a/src/AcDream.Core/Physics/TransitionTypes.cs +++ b/src/AcDream.Core/Physics/TransitionTypes.cs @@ -1911,10 +1911,29 @@ public sealed class Transition // iteration. Allocate per call (cheap — typically 0-5 entries). var nearbyObjs = new List(); float queryRadius = sphereRadius + movement.Length() + 5f; + + // Issue #91 (2026-05-20): interior items (fireplaces, tables, chests) + // are registered with `cellScope = ParentCellId` per A1.5's fix at + // `4d3bf6f`. They're stored under the indoor cell key (e.g. + // `0xA9B40121`), but GetNearbyObjects's outdoor-grid lookup (cells + // like `0xA9B40029`) never queries that key. Compute the indoor + // candidate set via CellTransit.FindCellSet — same set A4 uses for + // multi-cell BSP iteration — and pass it through so indoor shadows + // are also picked up. When the seed is outdoor the set typically + // contains only outdoor land-cells which the new branch in + // GetNearbyObjects skips via the `< 0x0100u` filter, so behavior + // matches the prior outdoor-only path. + // (engine.DataCache is non-null per the early-return at top of + // FindObjCollisions; redundant inner check would confuse nullable + // flow analysis.) + _ = CellTransit.FindCellSet(engine.DataCache, currPos, sphereRadius, + sp.CheckCellId, out var indoorCellIds); + engine.ShadowObjects.GetNearbyObjects( currPos, queryRadius, worldOffsetX, worldOffsetY, landblockId, - nearbyObjs); + nearbyObjs, + indoorCellIds); foreach (var obj in nearbyObjs) {