b451f83174
Captures the pre-Godot-port state of the codebase. This is the rollback anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md). All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
135 lines
7.0 KiB
C#
135 lines
7.0 KiB
C#
namespace Theriapolis.Core.Data;
|
|
|
|
/// <summary>
|
|
/// Pre-loaded content lookup tables. Constructing one calls every loader
|
|
/// exactly once and indexes results by id, so subsequent
|
|
/// <c>resolver.Clades["canidae"]</c> lookups are O(1).
|
|
///
|
|
/// Used by character creation, save/load (id → def resolution), and Phase 5 M5
|
|
/// NPC instantiation. Shared across screens that need any combination of
|
|
/// these tables.
|
|
/// </summary>
|
|
public sealed class ContentResolver
|
|
{
|
|
public IReadOnlyDictionary<string, CladeDef> Clades { get; }
|
|
public IReadOnlyDictionary<string, SpeciesDef> Species { get; }
|
|
public IReadOnlyDictionary<string, ClassDef> Classes { get; }
|
|
public IReadOnlyDictionary<string, SubclassDef> Subclasses { get; }
|
|
public IReadOnlyDictionary<string, BackgroundDef> Backgrounds { get; }
|
|
public IReadOnlyDictionary<string, ItemDef> Items { get; }
|
|
public IReadOnlyDictionary<string, LootTableDef> LootTables { get; }
|
|
public NpcTemplateContent Npcs { get; }
|
|
|
|
/// <summary>Phase 6 M0 — building templates + settlement layouts.</summary>
|
|
public SettlementContent Settlements { get; }
|
|
|
|
/// <summary>Phase 6 M1 — pre-meeting bias profiles per <c>reputation.md §I-1</c>.</summary>
|
|
public IReadOnlyDictionary<string, BiasProfileDef> BiasProfiles { get; }
|
|
|
|
/// <summary>Phase 6 M2 — faction definitions including opposition matrix entries.</summary>
|
|
public IReadOnlyDictionary<string, FactionDef> Factions { get; }
|
|
|
|
/// <summary>Phase 6 M1 — generic + named friendly/neutral resident templates.</summary>
|
|
public IReadOnlyDictionary<string, ResidentTemplateDef> Residents { get; }
|
|
|
|
/// <summary>
|
|
/// Phase 6 M1 — fast lookup of named residents by anchor-prefixed role
|
|
/// tag (e.g. "millhaven.innkeeper" → ResidentTemplateDef). Generic
|
|
/// templates live in <see cref="Residents"/>; this index only holds
|
|
/// the entries with <c>named: true</c>.
|
|
/// </summary>
|
|
public IReadOnlyDictionary<string, ResidentTemplateDef> ResidentsByRoleTag { get; }
|
|
|
|
/// <summary>Phase 6 M3 — dialogue trees indexed by id.</summary>
|
|
public IReadOnlyDictionary<string, DialogueDef> Dialogues { get; }
|
|
|
|
/// <summary>Phase 6 M4 — quest trees indexed by id.</summary>
|
|
public IReadOnlyDictionary<string, QuestDef> Quests { get; }
|
|
|
|
/// <summary>Phase 7 M0 — room templates indexed by id (every dungeon type).</summary>
|
|
public IReadOnlyDictionary<string, RoomTemplateDef> RoomTemplates { get; }
|
|
|
|
/// <summary>
|
|
/// Phase 7 M0 — room templates indexed by dungeon type (e.g. <c>imperium</c>
|
|
/// → IList of all imperium-typed templates). Used by the layout matcher
|
|
/// to filter candidates without a linear scan.
|
|
/// </summary>
|
|
public IReadOnlyDictionary<string, IReadOnlyList<RoomTemplateDef>> RoomTemplatesByType { get; }
|
|
|
|
/// <summary>Phase 7 M0 — dungeon layouts indexed by id.</summary>
|
|
public IReadOnlyDictionary<string, DungeonLayoutDef> DungeonLayouts { get; }
|
|
|
|
/// <summary>
|
|
/// Phase 7 M0 — anchor-locked layouts indexed by anchor id (e.g.
|
|
/// <c>OldHowlMine</c> → the pinned 3-room layout). Procedural pipeline
|
|
/// never picks anchor-locked layouts; the anchor resolver consults this
|
|
/// dict directly.
|
|
/// </summary>
|
|
public IReadOnlyDictionary<string, DungeonLayoutDef> DungeonLayoutsByAnchor { get; }
|
|
|
|
public ContentResolver(ContentLoader loader)
|
|
{
|
|
var clades = loader.LoadClades();
|
|
Clades = clades.ToDictionary(c => c.Id, StringComparer.OrdinalIgnoreCase);
|
|
var speciesArr = loader.LoadSpecies(clades);
|
|
Species = speciesArr.ToDictionary(s => s.Id, StringComparer.OrdinalIgnoreCase);
|
|
var classes = loader.LoadClasses();
|
|
Classes = classes.ToDictionary(c => c.Id, StringComparer.OrdinalIgnoreCase);
|
|
Subclasses = loader.LoadSubclasses(classes).ToDictionary(s => s.Id, StringComparer.OrdinalIgnoreCase);
|
|
Backgrounds = loader.LoadBackgrounds().ToDictionary(b => b.Id, StringComparer.OrdinalIgnoreCase);
|
|
var items = loader.LoadItems();
|
|
Items = items.ToDictionary(i => i.Id, StringComparer.OrdinalIgnoreCase);
|
|
LootTables = loader.LoadLootTables(items).ToDictionary(t => t.Id, StringComparer.OrdinalIgnoreCase);
|
|
|
|
// Phase 6 M5 — factions loaded early so NpcTemplates can validate
|
|
// their faction field references against the canonical list.
|
|
var factionsArr = loader.LoadFactions();
|
|
Factions = factionsArr.ToDictionary(f => f.Id, StringComparer.OrdinalIgnoreCase);
|
|
Npcs = loader.LoadNpcTemplates(items, factionsArr);
|
|
|
|
// Phase 6 M0 — building/layout content.
|
|
var buildings = loader.LoadBuildingTemplates();
|
|
var layouts = loader.LoadSettlementLayouts(buildings);
|
|
var byId = buildings.ToDictionary(b => b.Id, StringComparer.OrdinalIgnoreCase);
|
|
var preset = layouts.Where(l => l.Kind == "preset")
|
|
.ToDictionary(l => l.Anchor, StringComparer.OrdinalIgnoreCase);
|
|
var proc = layouts.Where(l => l.Kind == "procedural")
|
|
.ToDictionary(l => l.Tier);
|
|
Settlements = new SettlementContent(byId, preset, proc);
|
|
|
|
// Phase 6 M1 — bias profiles + resident templates.
|
|
// (factionsArr already loaded above for NpcTemplates validation.)
|
|
var biasArr = loader.LoadBiasProfiles(clades, factionsArr);
|
|
BiasProfiles = biasArr.ToDictionary(b => b.Id, StringComparer.OrdinalIgnoreCase);
|
|
var residentArr = loader.LoadResidentTemplates(biasArr, clades, speciesArr, factionsArr);
|
|
Residents = residentArr.ToDictionary(r => r.Id, StringComparer.OrdinalIgnoreCase);
|
|
ResidentsByRoleTag = residentArr
|
|
.Where(r => r.Named)
|
|
.ToDictionary(r => r.RoleTag, StringComparer.OrdinalIgnoreCase);
|
|
|
|
// Phase 6 M3 — dialogue trees.
|
|
Dialogues = loader.LoadDialogues(items)
|
|
.ToDictionary(d => d.Id, StringComparer.OrdinalIgnoreCase);
|
|
|
|
// Phase 6 M4 — quest trees.
|
|
Quests = loader.LoadQuests(items)
|
|
.ToDictionary(q => q.Id, StringComparer.OrdinalIgnoreCase);
|
|
|
|
// Phase 7 M0 — room templates + dungeon layouts.
|
|
var roomArr = loader.LoadRoomTemplates();
|
|
RoomTemplates = roomArr.ToDictionary(r => r.Id, StringComparer.OrdinalIgnoreCase);
|
|
RoomTemplatesByType = roomArr
|
|
.GroupBy(r => r.Type, StringComparer.OrdinalIgnoreCase)
|
|
.ToDictionary(
|
|
g => g.Key,
|
|
g => (IReadOnlyList<RoomTemplateDef>)g.ToArray(),
|
|
StringComparer.OrdinalIgnoreCase);
|
|
|
|
var layoutArr = loader.LoadDungeonLayouts(roomArr, LootTables.Values.ToArray());
|
|
DungeonLayouts = layoutArr.ToDictionary(l => l.Id, StringComparer.OrdinalIgnoreCase);
|
|
DungeonLayoutsByAnchor = layoutArr
|
|
.Where(l => !string.IsNullOrEmpty(l.Anchor))
|
|
.ToDictionary(l => l.Anchor, StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
}
|