using Theriapolis.Core.Rules.Stats; namespace Theriapolis.Game.UI; /// /// Author-curated UI text that the wizard surfaces but isn't worth pushing /// into JSON — skill descriptions, language metadata, ability labels, and /// the informational class↔clade recommendation table. /// /// Sourced from the Phase 5 character-creator design handoff /// (`_design_handoff/character_creation/` snapshot, src/data.jsx). Keeping /// these here rather than in Content/Data/ lets the design tone live /// next to the code that consumes it. /// public static class CodexCopy { /// STR/DEX/CON/INT/WIS/CHA → long display name. public static readonly System.Collections.Generic.IReadOnlyDictionary AbilityLabels = new System.Collections.Generic.Dictionary { [AbilityId.STR] = "Strength", [AbilityId.DEX] = "Dexterity", [AbilityId.CON] = "Constitution", [AbilityId.INT] = "Intellect", [AbilityId.WIS] = "Wisdom", [AbilityId.CHA] = "Charisma", }; /// SizeCategory snake_case → pretty label. public static string SizeLabel(string sizeKey) => sizeKey switch { "small" => "Small", "medium" => "Medium", "medium_large" => "Medium-Large", "large" => "Large", _ => sizeKey, }; /// Skill id (snake_case) → display name. public static string SkillName(string skillId) => skillId switch { "acrobatics" => "Acrobatics", "animal_handling" => "Animal Handling", "arcana" => "Arcana", "athletics" => "Athletics", "deception" => "Deception", "history" => "History", "insight" => "Insight", "intimidation" => "Intimidation", "investigation" => "Investigation", "medicine" => "Medicine", "nature" => "Nature", "perception" => "Perception", "performance" => "Performance", "persuasion" => "Persuasion", "religion" => "Religion", "sleight_of_hand" => "Sleight of Hand", "stealth" => "Stealth", "survival" => "Survival", _ => skillId, }; /// One-sentence skill description in the codex's tone (from the design's data.jsx). public static string SkillDescription(string skillId) => skillId switch { "acrobatics" => "Tumbling, balance, and the kind of footwork that keeps you upright on a coliseum sand-floor or a warren-rope. Body-cunning under pressure.", "animal_handling" => "Reading and steering non-sentient beasts — feral hounds, draft-kine, the wild cousins of your own clade.", "arcana" => "Knowledge of the older magics: scent-sorcery, blood-sigil, the half-forgotten rites that pre-date the Covenant of Claws.", "athletics" => "Raw physical effort. Climbing scaffold, swimming the foul canal, hauling a packmate from the pit, breaking a hold.", "deception" => "Speaking convincingly past your scent. The art of the false posture, the planted rumor, the answer that is technically true.", "history" => "The long memory of Theriapolis — the Imperium's fall, the Compact's ratification, which clade owes which other a centuries-old debt.", "insight" => "Reading another's true posture beneath their words. Catching the off-note in a snarl, the held breath, the lie in a friendly tail.", "intimidation" => "Bared-teeth diplomacy. The threat made plain enough that violence is not required to extract compliance.", "investigation" => "Methodical search and inference: scene-reading, document-sifting, the patient accumulation of small facts into a verdict.", "medicine" => "Field surgery, poultice-craft, knowing which clade tolerates which tincture. Stabilizing the dying without finishing them.", "nature" => "Knowledge of the wild outside the city wall — terrain, weather, plant-lore, and the unsigned beasts that observe no Covenant.", "perception" => "Awareness through every sense your clade gives you: ear-cock, scent-prickle, the half-glimpsed shape at the edge of vision.", "performance" => "Holding an audience — coliseum crowd, courtroom gallery, market square. Song, oratory, the body that compels watching.", "persuasion" => "Open-handed argument. The case made on its merits, the appeal to mutual benefit, the patient construction of agreement.", "religion" => "The hymn-cycles of the Cervid liturgy, the Compact's sacred clauses, the small household rites your clade keeps without thinking.", "sleight_of_hand" => "Quiet fingers — pickpocketing, palmed coins, the swap performed under another's nose.", "stealth" => "Movement unseen and unsmelled. Wind-checking, scent-suppression, the slow weight-shift on a creaking floor.", "survival" => "Field-craft beyond the wall: tracking, foraging, fire-making, knowing which run-off is safe to drink.", _ => "", }; /// Skill id → governing ability. public static AbilityId SkillAbility(string skillId) { try { return SkillIdExtensions.FromJson(skillId).Ability(); } catch { return AbilityId.STR; } } /// /// enum → snake_case JSON id. Inverse of /// . Required because /// enum.ToString().ToLowerInvariant() produces "sleightofhand" /// for SleightOfHand, which doesn't match the JSON-keyed /// dictionaries (skill name / skill description). /// public static string SkillIdToJson(SkillId s) => s switch { SkillId.Acrobatics => "acrobatics", SkillId.AnimalHandling => "animal_handling", SkillId.Arcana => "arcana", SkillId.Athletics => "athletics", SkillId.Deception => "deception", SkillId.History => "history", SkillId.Insight => "insight", SkillId.Intimidation => "intimidation", SkillId.Investigation => "investigation", SkillId.Medicine => "medicine", SkillId.Nature => "nature", SkillId.Perception => "perception", SkillId.Performance => "performance", SkillId.Persuasion => "persuasion", SkillId.Religion => "religion", SkillId.SleightOfHand => "sleight_of_hand", SkillId.Stealth => "stealth", SkillId.Survival => "survival", _ => s.ToString().ToLowerInvariant(), }; /// Language id → display name. public static string LanguageName(string langId) => langId switch { "common" => "Common", "canid" => "Canid", "felid" => "Felid", "mustelid" => "Mustelid", "ursid" => "Ursid", "cervid" => "Cervid", "bovid" => "Bovid", "leporid" => "Leporid", _ => langId, }; /// Language id → flavor description for hover/detail panels. public static string LanguageDescription(string langId) => langId switch { "common" => "The market-and-courthouse trade tongue of Theriapolis. Spoken by every clade.", "canid" => "Pack-tongue of the Canidae. Heavy with subsonic registers and scent-words non-Canid speakers cannot fully parse.", "felid" => "Sinuous and tonal, with a parallel tail-and-ear pidgin. Felid speakers trade in implication and pause.", "mustelid" => "Quick, percussive trade-speech of the Mustelidae. Famous for its dense vocabulary of musks, debts, and small grievances.", "ursid" => "Slow, low-register growl-speech. Ursid grammar prefers final emphasis — the important word always comes last.", "cervid" => "Old, hymn-shaped tongue of the Cervidae. Most speakers know Cervid as a song-language for funerals, treaties, and the long calendar.", "bovid" => "Patient, formal speech of the herd-clades. The language of guild-councils and oaths.", "leporid" => "Rapid, twitch-paced chatter of the Leporidae. Uses tense markers for danger and runs faster than most non-Leporidae can follow.", _ => "", }; /// Item id → pretty display name (mirrors items.json's name for the subset used by starting kits). public static string ItemName(string itemId) => itemId switch { "rend_sword" => "Rend-sword", "chain_shirt" => "Chain Shirt", "buckler" => "Buckler", "healers_kit" => "Healer's Kit", "rations_predator" => "Rations (predator)", "rations_prey" => "Rations (prey)", "hoof_club" => "Hoof Club", "chain_mail" => "Chain Mail", "standard_shield" => "Standard Shield", "paw_axe" => "Paw-axe", "hide_vest" => "Hide Vest", "thorn_blade" => "Thorn-blade", "studded_leather" => "Studded Leather", "claw_bow" => "Claw-bow", "poultice_universal" => "Universal Poultice", "scent_mask_basic" => "Basic Scent-mask", "fang_knife" => "Fang-knife", "leather_harness" => "Leather Harness", "pheromone_vial_calm" => "Pheromone Vial (calm)", "pheromone_vial_fear" => "Pheromone Vial (fear)", "rope_claw_braid" => "Claw-braid Rope", _ => itemId, }; /// /// Class id → list of clade ids that suit it. Drives the "★ Suits Clade" /// recommendation badge on the calling step. Informational only — the /// player can still pick any class with any clade. /// public static readonly System.Collections.Generic.IReadOnlyDictionary ClassCladeRecommendations = new System.Collections.Generic.Dictionary { ["fangsworn"] = new[] { "canidae", "felidae", "ursidae" }, ["bulwark"] = new[] { "bovidae", "ursidae" }, ["feral"] = new[] { "ursidae", "mustelidae", "bovidae" }, ["shadow_pelt"] = new[] { "felidae", "mustelidae", "leporidae" }, ["scent_broker"] = new[] { "canidae", "mustelidae" }, ["covenant_keeper"] = new[] { "canidae", "bovidae", "cervidae" }, ["muzzle_speaker"] = new[] { "felidae", "leporidae" }, ["claw_wright"] = new[] { "mustelidae", "leporidae" }, }; /// Returns true if is one of the recommended clades for . public static bool IsSuited(string classId, string cladeId) { if (!ClassCladeRecommendations.TryGetValue(classId, out var clades)) return false; foreach (var c in clades) if (string.Equals(c, cladeId, System.StringComparison.OrdinalIgnoreCase)) return true; return false; } /// STR/DEX/.../CHA in canonical order (matches the design's `ABILITIES`). public static readonly AbilityId[] AbilityOrder = new[] { AbilityId.STR, AbilityId.DEX, AbilityId.CON, AbilityId.INT, AbilityId.WIS, AbilityId.CHA, }; /// Roman numeral 1..7 for stepper labels ("Folio I of VII"). public static string Romanize(int n) => n switch { 1 => "I", 2 => "II", 3 => "III", 4 => "IV", 5 => "V", 6 => "VI", 7 => "VII", 8 => "VIII", _ => n.ToString(), }; /// "+N" for n >= 0, "-N" otherwise. public static string Signed(int n) => n >= 0 ? $"+{n}" : n.ToString(); }