feat(core): UCG W2 Task 1 — ResolveCellId writes CellGraph.CurrCell (additive)
Add private `SetCurrAndReturn(uint)` helper in PhysicsEngine that looks up the resolved id in `DataCache.CellGraph` and writes `CurrCell` when the cell is present. Wrap the four RESOLVED-id return sites in ResolveCellId: - indoor no-CellBSP return (trust FindCellList) - indoor sphere-overlaps-CellBSP return - outdoor→indoor building-transit return (foreach candidate) - outdoor terrain-grid return The final no-match `return fallbackCellId;` is intentionally NOT wrapped — stale beats null (the caller's seed is preserved unchanged). CurrCell has zero readers in src/ (verified by ripgrep); this is additive write-only, identical observable behavior to W1. One new unit test (CellGraphMembershipTests) proves RED→GREEN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
83c452b87f
commit
0e27a6cc3f
2 changed files with 56 additions and 4 deletions
|
|
@ -256,6 +256,19 @@ public sealed class PhysicsEngine
|
|||
/// Design: <c>docs/superpowers/specs/2026-05-19-indoor-portal-cell-tracking-design.md</c>
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// UCG W2 Task 1: record the resolved cell as the single membership answer. Additive —
|
||||
/// CurrCell is written here; it has no reader yet (render-read is a later task).
|
||||
/// Leaves CurrCell unchanged when the id can't be resolved in the graph (stale beats null).
|
||||
/// Retail anchor: CPhysicsObj::set_cell_id (acclient_2013_pseudo_c.txt).
|
||||
/// </summary>
|
||||
private uint SetCurrAndReturn(uint resolvedId)
|
||||
{
|
||||
if (DataCache?.CellGraph is { } cg && cg.GetVisible(resolvedId) is { } cell)
|
||||
cg.CurrCell = cell;
|
||||
return resolvedId;
|
||||
}
|
||||
|
||||
internal uint ResolveCellId(Vector3 worldPos, float sphereRadius, uint fallbackCellId)
|
||||
{
|
||||
if (fallbackCellId == 0) return 0;
|
||||
|
|
@ -307,7 +320,7 @@ public sealed class PhysicsEngine
|
|||
// optionally re-enter an indoor cell via CheckBuildingTransit.
|
||||
var indoorCell = DataCache.GetCellStruct(indoorResult);
|
||||
if (indoorCell?.CellBSP?.Root is null)
|
||||
return indoorResult; // Can't verify (no CellBSP); trust FindCellList.
|
||||
return SetCurrAndReturn(indoorResult); // Can't verify (no CellBSP); trust FindCellList.
|
||||
|
||||
// Issue #90 fix (2026-05-20): use SPHERE-overlap instead of POINT-in
|
||||
// for the indoor verification. The previous point-only check caused
|
||||
|
|
@ -325,7 +338,7 @@ public sealed class PhysicsEngine
|
|||
// BSPTREE::sphere_intersects_cell_bsp at :323267.
|
||||
var localCenter = Vector3.Transform(worldPos, indoorCell.InverseWorldTransform);
|
||||
if (BSPQuery.SphereIntersectsCellBsp(indoorCell.CellBSP.Root, localCenter, sphereRadius))
|
||||
return indoorResult;
|
||||
return SetCurrAndReturn(indoorResult);
|
||||
|
||||
// Fall through to outdoor resolution: player has FULLY left the
|
||||
// indoor portal-connected graph (sphere no longer overlaps).
|
||||
|
|
@ -359,12 +372,12 @@ public sealed class PhysicsEngine
|
|||
{
|
||||
// First candidate wins — building portal containment is
|
||||
// mutually exclusive in retail (one interior cell per portal).
|
||||
foreach (var c in candidates) return c;
|
||||
foreach (var c in candidates) return SetCurrAndReturn(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outdoorCellId;
|
||||
return SetCurrAndReturn(outdoorCellId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
39
tests/AcDream.Core.Tests/Physics/CellGraphMembershipTests.cs
Normal file
39
tests/AcDream.Core.Tests/Physics/CellGraphMembershipTests.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.World.Cells;
|
||||
using DatReaderWriter.Types;
|
||||
using Xunit;
|
||||
using DatEnvCell = DatReaderWriter.DBObjs.EnvCell;
|
||||
|
||||
namespace AcDream.Core.Tests.Physics;
|
||||
|
||||
public class CellGraphMembershipTests
|
||||
{
|
||||
[Fact]
|
||||
public void ResolveCellId_Resolved_WritesCurrCellTrackingTheResolvedId()
|
||||
{
|
||||
var engine = new PhysicsEngine();
|
||||
var cache = new PhysicsDataCache();
|
||||
engine.DataCache = cache;
|
||||
|
||||
var cs = new CellStruct {
|
||||
VertexArray = new VertexArray { Vertices = new Dictionary<ushort, SWVertex>() },
|
||||
Polygons = new Dictionary<ushort, Polygon>(),
|
||||
PhysicsBSP = null,
|
||||
};
|
||||
var dat = new DatEnvCell {
|
||||
Flags = (DatReaderWriter.Enums.EnvCellFlags)0,
|
||||
CellPortals = new List<DatReaderWriter.Types.CellPortal>(),
|
||||
VisibleCells = new List<ushort>(),
|
||||
};
|
||||
cache.CacheCellStruct(0xA9B40174u, dat, cs, Matrix4x4.Identity); // registers in the graph (W1)
|
||||
|
||||
uint result = engine.ResolveCellId(new Vector3(0, 0, 0), 0.5f, 0xA9B40174u);
|
||||
|
||||
// CurrCell tracks whatever id ResolveCellId returned (when that id is in the graph).
|
||||
Assert.NotNull(cache.CellGraph.CurrCell);
|
||||
Assert.Equal(result, cache.CellGraph.CurrCell!.Id);
|
||||
Assert.Equal(0xA9B40174u, cache.CellGraph.CurrCell!.Id);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue