Files
TheriapolisV3/Theriapolis.Core/Data/ResidentTemplateDef.cs
Christopher Wiebe b451f83174 Initial commit: Theriapolis baseline at port/godot branch point
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>
2026-04-30 20:40:51 -07:00

84 lines
3.7 KiB
C#

using System.Text.Json.Serialization;
namespace Theriapolis.Core.Data;
/// <summary>
/// Phase 6 M1 — definition of a friendly/neutral resident NPC inhabiting a
/// settlement. Two flavours, both loaded from <c>resident_templates.json</c>:
///
/// 1. **Generic** (<c>Named == false</c>) — matches by <see cref="RoleTag"/>
/// prefix. <see cref="ResidentInstantiator"/> picks the highest-weight
/// generic whose RoleTag equals the building-role tag (e.g. "innkeeper",
/// "shopkeeper", "guard").
///
/// 2. **Named** (<c>Named == true</c>) — matches by exact <see cref="RoleTag"/>
/// (e.g. "millhaven.innkeeper"). Used when a settlement preset's
/// <c>role_overrides</c> qualifies a building role with a specific
/// anchor-prefixed tag, locking that NPC to a hand-authored species,
/// name, and bias profile.
///
/// Combat stats are minimal in M1 — residents are non-combatants by
/// default. They have a token <see cref="Hp"/>/<see cref="Ac"/> in case the
/// player attacks them; engagement promotes them to a derived combatant
/// using the existing <see cref="NpcTemplateDef"/>-style stat block.
/// </summary>
public sealed record ResidentTemplateDef
{
[JsonPropertyName("id")]
public string Id { get; init; } = "";
/// <summary>
/// The role tag this template matches. Generic templates use bare
/// occupations ("innkeeper"); named templates use anchor-prefixed
/// "settlement.role" ids ("millhaven.innkeeper").
/// </summary>
[JsonPropertyName("role_tag")]
public string RoleTag { get; init; } = "";
/// <summary>True when this template is hand-authored for a specific named NPC. Always wins over generic when role_tag matches.</summary>
[JsonPropertyName("named")]
public bool Named { get; init; } = false;
/// <summary>Display name shown in dialogue + tooltip. Empty for generics → resolved from <see cref="RoleTag"/>.</summary>
[JsonPropertyName("name")]
public string Name { get; init; } = "";
/// <summary>Clade id (e.g. "canidae"). Required for named templates; generics may leave empty to roll from settlement biome.</summary>
[JsonPropertyName("clade")]
public string Clade { get; init; } = "";
/// <summary>Species id (e.g. "wolf"). Required for named; generics roll from clade.</summary>
[JsonPropertyName("species")]
public string Species { get; init; } = "";
/// <summary>Bias profile id this NPC carries (matches BiasProfileDef.Id).</summary>
[JsonPropertyName("bias_profile")]
public string BiasProfile { get; init; } = "URBAN_PROGRESSIVE";
/// <summary>Faction affiliation id (matches FactionDef.Id), or empty for unaffiliated.</summary>
[JsonPropertyName("faction")]
public string Faction { get; init; } = "";
/// <summary>Dialogue tree id (matches dialogues/*.json id). Empty → fall back to a generic-by-role placeholder.</summary>
[JsonPropertyName("dialogue")]
public string Dialogue { get; init; } = "";
/// <summary>"friendly" or "neutral". Defaults to friendly. Hostile residents go through the npc_templates path instead.</summary>
[JsonPropertyName("default_allegiance")]
public string DefaultAllegiance { get; init; } = "friendly";
/// <summary>Stat block for combat fallback. Defaults are commoner-ish (HP 8, AC 10).</summary>
[JsonPropertyName("hp")]
public int Hp { get; init; } = 8;
[JsonPropertyName("ac")]
public int Ac { get; init; } = 10;
[JsonPropertyName("ability_scores")]
public Dictionary<string, int> AbilityScores { get; init; } = new();
/// <summary>Selection weight when multiple generic templates match the same role tag.</summary>
[JsonPropertyName("weight")]
public float Weight { get; init; } = 1f;
}