feat(phys): A6.P4 slice 1 — portal-reachable cellSet includes outdoor cells
Closes #99 (run-through doors regression fromb3ce505). Theb3ce505stopgap for #98 gates the outdoor 24m radial sweep on indoor primary cells. Combined with ShadowObjectRegistry.GetNearbyObjects' "skip outdoor ids" filter on the cellScope-pass loop, this meant doors registered at outdoor cells (default cellScope=0u for server-spawned entities at GameWindow.cs:3139) were invisible to spheres on the indoor side of a doorway threshold — walk-through. Pre-flight reads found that CellTransit.FindCellSet already adds outdoor cells to its candidate set when the sphere straddles an OtherCellId=0xFFFF exit portal (via AddAllOutsideCells triggered by exitOutside=true inside the indoor-seed BFS). The fix is to stop filtering those outdoor ids out before iterating, and rename the param to portalReachableCells to reflect what the set actually contains. - Q1: Indoor EnvCell.VisibleCellIds is indoor-only in all 16 cottage fixtures (low 16 bits ≥ 0x0100). OtherCellId=0xFFFF on portals marks "exit to outdoor world" without naming a specific cellId; the specific outdoor cell is computed by AddAllOutsideCells from world XY when the sphere straddles the exit portal. - Q2: GameWindow.cs:3139 ShadowObjects.Register for server-spawned entities passes no cellScope → default 0u → outdoor 24m grid registration. UpdatePosition (line 145) does the same on movement. Doors are confirmed outdoor-registered. Slice 1 makes a smaller change than the spec proposed (no new parameter; just drop the existing filter), because FindCellSet's existing exit-portal logic already exposes the needed outdoor cells. The retail-faithful registration-side BuildShadowCellSet refactor and theb3ce505gate removal stay scheduled for slices 2-3. Verification: - 24/24 ShadowObjectRegistryTests pass (incl. two new slice 1 tests: IndoorPrimary_OutdoorCellInPortalSet_DoorReturned closes #99; IndoorPrimary_IndoorOnlyPortalSet_OutdoorRadialStillSkipped regression-pins #98) - 11/11 CellarUpTrajectoryReplayTests pass (LiveCompare_FirstCap_ FixClosesCottageFloorCap stays green) - dotnet build AcDream.slnx: 0 errors, 0 warnings - Pre-existing 6-8 static-state-leakage failures in serial physics suite verified unchanged by stash+retest baseline check Visual verification pending: walk Holtburg cottage doorway from both sides; door blocks both directions; cellar still climbable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3e3cd77202
commit
b49ed904c3
3 changed files with 122 additions and 30 deletions
|
|
@ -301,4 +301,73 @@ public class ShadowObjectRegistryTests
|
|||
Assert.Equal(0x00000004u, inA.State);
|
||||
Assert.Equal(0x00000004u, inB.State);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// A6.P4 slice 1 — portalReachableCells set includes outdoor cells
|
||||
// when sphere straddles an exit portal (issue #99)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void GetNearbyObjects_PortalReachableSetIncludesOutdoorCell_IndoorPrimary_DoorReturned()
|
||||
{
|
||||
// A6.P4 slice 1 / issue #99 (2026-05-24): when the player's sphere is
|
||||
// in an indoor cell but its CellTransit.FindCellSet result includes
|
||||
// an outdoor cell (via AddAllOutsideCells triggered by the sphere
|
||||
// straddling an OtherCellId=0xFFFF exit portal), GetNearbyObjects
|
||||
// must return shadows registered in that outdoor cell. Doors are
|
||||
// server-spawned entities registered at GameWindow.cs:3139 with
|
||||
// default cellScope=0u → outdoor 24-m grid registration. Before this
|
||||
// fix, the loop filtered out outdoor cell ids ("skip outdoor ids")
|
||||
// AND #98's primaryCellId gate skipped the radial sweep — net result
|
||||
// was that doors at building thresholds were invisible to spheres
|
||||
// on the indoor side: walk-through.
|
||||
var reg = new ShadowObjectRegistry();
|
||||
|
||||
const uint doorEntityId = 0x000F4244u;
|
||||
reg.Register(doorEntityId, 0x020019FFu, new Vector3(12f, 12f, 50f),
|
||||
Quaternion.Identity, 1f, OffX, OffY, LbId,
|
||||
ShadowCollisionType.Cylinder, cylHeight: 2.5f);
|
||||
|
||||
var results = new List<ShadowEntry>();
|
||||
uint doorOutdoorCellId = LbId | 1u; // outdoor cell where door sits
|
||||
uint vestibuleCellId = LbId | 0x0145u; // indoor cell on the other side
|
||||
|
||||
// Sphere is in the vestibule; FindCellSet added the doorway's outdoor
|
||||
// cell via AddAllOutsideCells. Both ids land in portalReachableCells.
|
||||
reg.GetNearbyObjects(
|
||||
new Vector3(12f, 12f, 50f), 5f, OffX, OffY, LbId, results,
|
||||
portalReachableCells: new[] { vestibuleCellId, doorOutdoorCellId },
|
||||
primaryCellId: vestibuleCellId);
|
||||
|
||||
Assert.Contains(results, e => e.EntityId == doorEntityId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetNearbyObjects_IndoorPrimary_IndoorOnlyPortalSet_OutdoorRadialStillSkipped()
|
||||
{
|
||||
// Regression check for issue #98: when the FindCellSet result holds
|
||||
// only indoor cells (sphere not near an exit portal — e.g. deep in
|
||||
// a cellar), the radial outdoor sweep must stay skipped so the
|
||||
// landblock-baked cottage GfxObj (registered at outdoor cellScope=0)
|
||||
// is NOT returned to a sphere in the cellar EnvCell. Otherwise the
|
||||
// head sphere bumps the cottage's downward-facing floor poly from
|
||||
// below and caps the cellar ascent at world Z≈92.7.
|
||||
var reg = new ShadowObjectRegistry();
|
||||
|
||||
const uint cottageEntityId = 0xA9B47900u;
|
||||
reg.Register(cottageEntityId, 0x01000A2Bu, new Vector3(12f, 12f, 90f),
|
||||
Quaternion.Identity, 5.5f, OffX, OffY, LbId);
|
||||
|
||||
var results = new List<ShadowEntry>();
|
||||
uint cellarCellId = LbId | 0x0146u;
|
||||
|
||||
// Sphere is in the cellar; FindCellSet returned indoor-only set
|
||||
// (no exit portal nearby).
|
||||
reg.GetNearbyObjects(
|
||||
new Vector3(12f, 12f, 50f), 5.5f, OffX, OffY, LbId, results,
|
||||
portalReachableCells: new[] { cellarCellId },
|
||||
primaryCellId: cellarCellId);
|
||||
|
||||
Assert.DoesNotContain(results, e => e.EntityId == cottageEntityId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue