using System.Text.Json.Serialization; namespace Theriapolis.Core.Data; /// /// Phase 7 M0 — a single hand-authored dungeon room. Loaded from /// Content/Data/room_templates/<type>/*.json. /// /// A template describes: /// - The room's footprint in tactical tiles (perimeter walls included). /// - The 2D ASCII : one char per tactical tile. /// Legend (per Phase 7 plan §5.1): /// # wall, . floor, , rubble, D door slot, /// @ encounter slot, C container slot, T trap slot, /// P pillar, B brazier, M mosaic (narrative), /// S stairs (entry/exit only). /// - Door positions on the perimeter (one entry per D in the grid). /// - Encounter / container / trap slot positions (which the dungeon /// populator fills with NPCs and loot). /// - Optional narrative text surfaced by Scent Literacy / room-clear coda. /// - The clade that the room — drives the /// clade-responsive movement multiplier (Phase 7 plan §5.4). /// /// Templates are designer-friendly to author: edit ASCII art + a couple /// of metadata blocks. /// validates grid dimensions vs declared footprint, perimeter walls, /// and that every D/@/C/T in the grid has /// a matching slot record. /// public sealed record RoomTemplateDef { [JsonPropertyName("id")] public string Id { get; init; } = ""; [JsonPropertyName("name")] public string Name { get; init; } = ""; /// /// Dungeon type the template belongs to: imperium, mine, /// cult, cave, overgrown. Layout matchers filter /// templates by type when assembling a dungeon. /// [JsonPropertyName("type")] public string Type { get; init; } = ""; /// /// Clade that built or originally inhabited the room. Drives Phase 7 /// clade-responsive movement (a Large PC in a Mustelid tunnel takes 2× /// movement points). Allowed: canid, felid, mustelid, /// ursid, cervid, bovid, leporid, /// imperium, none. Phase-7 Imperium templates use /// imperium; templates that would be at home in any dungeon use /// none. /// [JsonPropertyName("built_by")] public string BuiltBy { get; init; } = "none"; /// /// Size class — small, medium, large. Used by /// the layout matcher to pick room mixes appropriate to the dungeon's /// size band (small dungeons prefer small rooms, etc.). /// [JsonPropertyName("size_class")] public string SizeClass { get; init; } = "medium"; /// /// Roles this template is eligible for: entry, transit, /// narrative, loot, boss, dead-end. A /// template can be eligible for multiple roles (a "pillar room" can /// serve as transit OR as a loot stash). /// [JsonPropertyName("roles_eligible")] public string[] RolesEligible { get; init; } = Array.Empty(); /// Footprint width in tactical tiles. Includes perimeter walls. Must equal Grid[*].Length. [JsonPropertyName("footprint_w_tiles")] public int FootprintWTiles { get; init; } = 1; /// Footprint height in tactical tiles. Includes perimeter walls. Must equal Grid.Length. [JsonPropertyName("footprint_h_tiles")] public int FootprintHTiles { get; init; } = 1; /// /// 2D ASCII art: one entry per row, one char per tactical tile. /// Validated for perimeter wall completeness and slot-coordinate /// matches at content-load time. /// [JsonPropertyName("grid")] public string[] Grid { get; init; } = Array.Empty(); /// Door positions in template-local coords (matches D chars in ). [JsonPropertyName("doors")] public RoomDoor[] Doors { get; init; } = Array.Empty(); /// Encounter slot positions (matches @ chars). [JsonPropertyName("encounter_slots")] public RoomEncounterSlot[] EncounterSlots { get; init; } = Array.Empty(); /// Container slot positions (matches C chars). [JsonPropertyName("container_slots")] public RoomContainerSlot[] ContainerSlots { get; init; } = Array.Empty(); /// Trap slot positions (matches T chars). Phase 7 ships only tripwire traps. [JsonPropertyName("trap_slots")] public RoomTrapSlot[] TrapSlots { get; init; } = Array.Empty(); /// Decoration placements for non-slot decos (P pillar, B brazier, M mosaic). [JsonPropertyName("decos")] public RoomDecoPlacement[] Decos { get; init; } = Array.Empty(); /// /// Environmental-story prose surfaced by Scent Literacy (Phase 6.5 M1) /// in the InteractionScreen scent-overlay panel and by the dungeon- /// clear coda. Null/empty for non-narrative templates. /// [JsonPropertyName("narrative_text")] public string? NarrativeText { get; init; } = null; /// Selection weight in layout assembly. Default 1.0. [JsonPropertyName("weight")] public float Weight { get; init; } = 1f; } public sealed record RoomDoor { [JsonPropertyName("x")] public int X { get; init; } [JsonPropertyName("y")] public int Y { get; init; } /// Compass facing: "N" / "E" / "S" / "W". Door always sits on a perimeter cell. [JsonPropertyName("facing")] public string Facing { get; init; } = "S"; /// /// Optional lock difficulty for this door. Empty = unlocked. Allowed: /// trivial, easy, medium, hard — mapped to /// LOCK_DC_* constants in code. /// [JsonPropertyName("lock")] public string Lock { get; init; } = ""; } public sealed record RoomEncounterSlot { [JsonPropertyName("x")] public int X { get; init; } [JsonPropertyName("y")] public int Y { get; init; } /// /// Spawn kind: PoiGuard / WildAnimal / Brigand / /// Boss. Resolved against npc_templates.json's /// per-dungeon-type spawn-kind override map at populate time. /// [JsonPropertyName("kind")] public string Kind { get; init; } = "PoiGuard"; /// Likelihood the slot fires when a layout calls for variability. 1.0 = always. [JsonPropertyName("weight")] public float Weight { get; init; } = 1f; } public sealed record RoomContainerSlot { [JsonPropertyName("x")] public int X { get; init; } [JsonPropertyName("y")] public int Y { get; init; } /// /// Loot-table band: t1 / t2 / t3. The dungeon's /// layout maps a band to a real loot-table id at populate time /// (). /// [JsonPropertyName("loot_table_band")] public string LootTableBand { get; init; } = "t1"; /// True when the container is locked (key required, or STR/DEX check). [JsonPropertyName("locked")] public bool Locked { get; init; } = false; /// Optional lock difficulty if : trivial/easy/medium/hard. [JsonPropertyName("lock")] public string Lock { get; init; } = ""; } public sealed record RoomTrapSlot { [JsonPropertyName("x")] public int X { get; init; } [JsonPropertyName("y")] public int Y { get; init; } /// Trap kind. Phase 7 ships only tripwire. [JsonPropertyName("kind")] public string Kind { get; init; } = "tripwire"; /// Disarm DC tier: trivial, easy, medium. [JsonPropertyName("disarm_dc")] public string DisarmDc { get; init; } = "easy"; } public sealed record RoomDecoPlacement { [JsonPropertyName("x")] public int X { get; init; } [JsonPropertyName("y")] public int Y { get; init; } /// /// Deco kind name. Allowed: pillar, brazier, mosaic, /// imperium_statue. Trap / container / door / stairs decos are /// declared via their respective slot collections, not here. /// [JsonPropertyName("deco")] public string Deco { get; init; } = ""; }