using System.Text.Json.Serialization;
namespace Theriapolis.Core.Data;
///
/// Immutable NPC stat-block template loaded from npc_templates.json.
/// Phase 5 instantiates one per per
/// chunk, with the actual template id chosen via the per-zone lookup
/// table on .
///
public sealed record NpcTemplateDef
{
[JsonPropertyName("id")]
public string Id { get; init; } = "";
[JsonPropertyName("name")]
public string Name { get; init; } = "";
/// Body size category, snake_case (small / medium / medium_large / large).
[JsonPropertyName("size")]
public string Size { get; init; } = "medium";
/// STR/DEX/CON/INT/WIS/CHA absolute values (10 = average).
[JsonPropertyName("ability_scores")]
public Dictionary AbilityScores { get; init; } = new();
[JsonPropertyName("hp")]
public int Hp { get; init; } = 1;
[JsonPropertyName("ac")]
public int Ac { get; init; } = 10;
[JsonPropertyName("speed_ft")]
public int SpeedFt { get; init; } = 30;
[JsonPropertyName("attacks")]
public NpcAttack[] Attacks { get; init; } = Array.Empty();
/// Behavior id ("brigand", "wild_animal", "poi_guard"). Maps to INpcBehavior in Phase 5 M5.
[JsonPropertyName("behavior")]
public string Behavior { get; init; } = "brigand";
/// Starts as Hostile / Neutral / Friendly. Phase 5 reads this on instantiation.
[JsonPropertyName("default_allegiance")]
public string DefaultAllegiance { get; init; } = "hostile";
///
/// Phase 6 M5 โ faction id this NPC owes allegiance to (matches
/// FactionDef.Id). Empty for unaligned templates (wild animals,
/// brigands). Drives M5 patrol-aggression: a non-hostile NPC with a
/// faction flips to Hostile when the player's local standing with
/// that faction crosses the HOSTILE threshold.
///
[JsonPropertyName("faction")]
public string Faction { get; init; } = "";
/// Loot table id (Phase 5 ships ~5; lookup deferred to M6).
[JsonPropertyName("loot_table")]
public string LootTable { get; init; } = "";
[JsonPropertyName("xp_award")]
public int XpAward { get; init; } = 0;
}
public sealed record NpcAttack
{
[JsonPropertyName("name")]
public string Name { get; init; } = "";
[JsonPropertyName("to_hit")]
public int ToHit { get; init; } = 0;
/// Damage dice expression (e.g. "1d6+2", "2d8").
[JsonPropertyName("damage")]
public string Damage { get; init; } = "";
[JsonPropertyName("damage_type")]
public string DamageType { get; init; } = "bludgeoning";
[JsonPropertyName("reach_tiles")]
public int ReachTiles { get; init; } = 1;
[JsonPropertyName("range_short_tiles")]
public int RangeShortTiles { get; init; } = 0;
[JsonPropertyName("range_long_tiles")]
public int RangeLongTiles { get; init; } = 0;
}
///
/// Top-level wrapper for npc_templates.json: the template list plus the
/// per-spawnkind, per-zone template-id lookup table.
///
public sealed record NpcTemplateContent
{
[JsonPropertyName("templates")]
public NpcTemplateDef[] Templates { get; init; } = Array.Empty();
///
/// SpawnKind name (e.g. "Brigand") โ array of template ids indexed by
/// DangerZone (0..4). Length should equal C.DANGER_ZONE_MAX + 1.
///
[JsonPropertyName("spawn_kind_to_template_by_zone")]
public Dictionary SpawnKindToTemplateByZone { get; init; } = new();
///
/// Phase 7 M2 โ per-dungeon-type override. Resolves a
/// (PoiType, SpawnKind) pair to a single template id, used by
/// when filling
/// in-room encounter slots. Per Phase 7 plan ยง10 open-decision #6:
/// DungeonType supersedes DangerZone entirely once the player is inside
/// a dungeon, so this map's value is a single template id (no zone
/// indexing). Outer key matches the enum
/// name (e.g. "ImperiumRuin"); inner key is the spawn-kind name
/// (e.g. "PoiGuard" / "WildAnimal" / "Brigand" /
/// "Boss"). Missing keys fall back to
/// at DangerZone=2 mid.
///
[JsonPropertyName("spawn_kind_to_template_by_dungeon_type")]
public Dictionary> SpawnKindToTemplateByDungeonType { get; init; } = new();
}