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)
{