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>
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
using Theriapolis.Core.Rules.Stats;
|
||||
|
||||
namespace Theriapolis.Game.UI;
|
||||
|
||||
/// <summary>
|
||||
/// 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 <c>Content/Data/</c> lets the design tone live
|
||||
/// next to the code that consumes it.
|
||||
/// </summary>
|
||||
public static class CodexCopy
|
||||
{
|
||||
/// <summary>STR/DEX/CON/INT/WIS/CHA → long display name.</summary>
|
||||
public static readonly System.Collections.Generic.IReadOnlyDictionary<AbilityId, string> AbilityLabels =
|
||||
new System.Collections.Generic.Dictionary<AbilityId, string>
|
||||
{
|
||||
[AbilityId.STR] = "Strength",
|
||||
[AbilityId.DEX] = "Dexterity",
|
||||
[AbilityId.CON] = "Constitution",
|
||||
[AbilityId.INT] = "Intellect",
|
||||
[AbilityId.WIS] = "Wisdom",
|
||||
[AbilityId.CHA] = "Charisma",
|
||||
};
|
||||
|
||||
/// <summary>SizeCategory snake_case → pretty label.</summary>
|
||||
public static string SizeLabel(string sizeKey) => sizeKey switch
|
||||
{
|
||||
"small" => "Small",
|
||||
"medium" => "Medium",
|
||||
"medium_large" => "Medium-Large",
|
||||
"large" => "Large",
|
||||
_ => sizeKey,
|
||||
};
|
||||
|
||||
/// <summary>Skill id (snake_case) → display name.</summary>
|
||||
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,
|
||||
};
|
||||
|
||||
/// <summary>One-sentence skill description in the codex's tone (from the design's data.jsx).</summary>
|
||||
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.",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
/// <summary>Skill id → governing ability.</summary>
|
||||
public static AbilityId SkillAbility(string skillId)
|
||||
{
|
||||
try { return SkillIdExtensions.FromJson(skillId).Ability(); }
|
||||
catch { return AbilityId.STR; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SkillId"/> enum → snake_case JSON id. Inverse of
|
||||
/// <see cref="SkillIdExtensions.FromJson"/>. Required because
|
||||
/// <c>enum.ToString().ToLowerInvariant()</c> produces "sleightofhand"
|
||||
/// for <c>SleightOfHand</c>, which doesn't match the JSON-keyed
|
||||
/// dictionaries (skill name / skill description).
|
||||
/// </summary>
|
||||
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(),
|
||||
};
|
||||
|
||||
/// <summary>Language id → display name.</summary>
|
||||
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,
|
||||
};
|
||||
|
||||
/// <summary>Language id → flavor description for hover/detail panels.</summary>
|
||||
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.",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
/// <summary>Item id → pretty display name (mirrors items.json's <c>name</c> for the subset used by starting kits).</summary>
|
||||
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,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public static readonly System.Collections.Generic.IReadOnlyDictionary<string, string[]> ClassCladeRecommendations =
|
||||
new System.Collections.Generic.Dictionary<string, string[]>
|
||||
{
|
||||
["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" },
|
||||
};
|
||||
|
||||
/// <summary>Returns true if <paramref name="cladeId"/> is one of the recommended clades for <paramref name="classId"/>.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>STR/DEX/.../CHA in canonical order (matches the design's `ABILITIES`).</summary>
|
||||
public static readonly AbilityId[] AbilityOrder = new[]
|
||||
{
|
||||
AbilityId.STR, AbilityId.DEX, AbilityId.CON, AbilityId.INT, AbilityId.WIS, AbilityId.CHA,
|
||||
};
|
||||
|
||||
/// <summary>Roman numeral 1..7 for stepper labels ("Folio I of VII").</summary>
|
||||
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(),
|
||||
};
|
||||
|
||||
/// <summary>"+N" for n >= 0, "-N" otherwise.</summary>
|
||||
public static string Signed(int n) => n >= 0 ? $"+{n}" : n.ToString();
|
||||
}
|
||||
Reference in New Issue
Block a user