New per-landblock data model for WB-style per-building cell scoping:
Building — BuildingId, EnvCellIds, ExitPortalPolygons,
occlusion-query state (Step 5 lifecycle)
BuildingRegistry — two-way indexed (by cellId + by buildingId);
single source of truth per landblock
BuildingLoader — static factory from LandBlockInfo.Buildings;
walks interior portals to expand cell sets;
collects exit portal polygons in world space
10 new unit tests cover data invariants + registry indexing + loader
mapping per the algorithm resolved in RR2 findings.
LoadedCell.BuildingId stamping wired in RR4. Render-time consumption
arrives in RR7 (Steps 1-4) + RR9 (Step 5) + RR11 (RenderOutsideIn).
Design: docs/superpowers/specs/2026-05-26-phase-a8-wb-full-port-design.md
Spike: docs/research/2026-05-26-a8-buildings-data-shape.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
73 lines
3.2 KiB
C#
73 lines
3.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace AcDream.App.Rendering.Wb;
|
|
|
|
/// <summary>
|
|
/// Phase A8 (2026-05-26): per-landblock registry of <see cref="Building"/>s.
|
|
/// Two-way indexed for O(1) cell→building and building-id→building lookups.
|
|
/// Built once per landblock at load time by <see cref="BuildingLoader"/>;
|
|
/// no mutations occur after initial population.
|
|
///
|
|
/// <para>The cell→building index uses a <c>List<Building></c> value type
|
|
/// to handle the (rare but valid) case where two buildings share an EnvCell —
|
|
/// each building performs its own BFS so a shared boundary cell ends up in both
|
|
/// <c>EnvCellIds</c> sets. <see cref="GetBuildingsContainingCell"/> returns all
|
|
/// owners so RR7's render path can pick the correct one.</para>
|
|
///
|
|
/// <para>WB reference: <c>WorldBuilder.Shared/Services/PortalService.cs</c>
|
|
/// (<c>BuildingPortalGroup</c>). Design:
|
|
/// <c>docs/superpowers/specs/2026-05-26-phase-a8-wb-full-port-design.md</c>.</para>
|
|
/// </summary>
|
|
public sealed class BuildingRegistry
|
|
{
|
|
// Index 1: cell-id → list of buildings containing that cell.
|
|
// Cells may belong to multiple buildings (rare; handled via List<Building>).
|
|
private readonly Dictionary<uint, List<Building>> _byCellId = new();
|
|
|
|
// Index 2: building-id → Building.
|
|
private readonly Dictionary<uint, Building> _byBuildingId = new();
|
|
|
|
/// <summary>
|
|
/// Adds a building to both indexes. Idempotent if the exact same
|
|
/// <see cref="Building"/> instance is added twice with the same
|
|
/// <see cref="Building.BuildingId"/>.
|
|
/// </summary>
|
|
/// <param name="b">The building to register.</param>
|
|
public void Add(Building b)
|
|
{
|
|
if (_byBuildingId.TryGetValue(b.BuildingId, out var existing) && ReferenceEquals(existing, b))
|
|
return;
|
|
_byBuildingId[b.BuildingId] = b;
|
|
foreach (var cellId in b.EnvCellIds)
|
|
{
|
|
if (!_byCellId.TryGetValue(cellId, out var list))
|
|
{
|
|
list = new List<Building>();
|
|
_byCellId[cellId] = list;
|
|
}
|
|
if (!list.Contains(b)) list.Add(b);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the buildings containing <paramref name="cellId"/>.
|
|
/// Returns an empty read-only list when the cell isn't part of any
|
|
/// building (outdoor cells, dungeon cells not tagged by
|
|
/// <c>LandBlockInfo.Buildings</c>).
|
|
/// </summary>
|
|
/// <param name="cellId">Full 32-bit cell id.</param>
|
|
public IReadOnlyList<Building> GetBuildingsContainingCell(uint cellId) =>
|
|
_byCellId.TryGetValue(cellId, out var list) ? list : Array.Empty<Building>();
|
|
|
|
/// <summary>Returns the building with the given id, or <c>null</c> if not found.</summary>
|
|
/// <param name="buildingId">The building id as allocated by <see cref="BuildingLoader"/>.</param>
|
|
public Building? GetById(uint buildingId) =>
|
|
_byBuildingId.TryGetValue(buildingId, out var b) ? b : null;
|
|
|
|
/// <summary>Enumerates every registered building in unspecified order.</summary>
|
|
public IEnumerable<Building> All() => _byBuildingId.Values;
|
|
|
|
/// <summary>Number of registered buildings.</summary>
|
|
public int Count => _byBuildingId.Count;
|
|
}
|