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:
Christopher Wiebe
2026-04-30 20:40:51 -07:00
commit b451f83174
525 changed files with 75786 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
using System.Text.Json.Serialization;
namespace Theriapolis.Core.Data;
/// <summary>
/// Immutable class definition loaded from classes.json. Phase 5 reads
/// every field — including the full level table — but only level-1
/// features have runtime effect; higher-level entries are forward-compat
/// scaffolding for the level-up flow shipped in Phase 5.5 / 6.
/// </summary>
public sealed record ClassDef
{
[JsonPropertyName("id")]
public string Id { get; init; } = "";
[JsonPropertyName("name")]
public string Name { get; init; } = "";
/// <summary>Hit die size: 6 / 8 / 10 / 12.</summary>
[JsonPropertyName("hit_die")]
public int HitDie { get; init; } = 8;
/// <summary>Primary ability key(s) (STR / DEX / CON / INT / WIS / CHA).</summary>
[JsonPropertyName("primary_ability")]
public string[] PrimaryAbility { get; init; } = Array.Empty<string>();
/// <summary>Saving-throw proficiencies.</summary>
[JsonPropertyName("saves")]
public string[] Saves { get; init; } = Array.Empty<string>();
/// <summary>Armor proficiency tags: "light", "medium", "heavy", "shields".</summary>
[JsonPropertyName("armor_proficiencies")]
public string[] ArmorProficiencies { get; init; } = Array.Empty<string>();
/// <summary>Weapon proficiency tags: "simple", "martial", "natural", or specific item ids.</summary>
[JsonPropertyName("weapon_proficiencies")]
public string[] WeaponProficiencies { get; init; } = Array.Empty<string>();
/// <summary>Tool proficiency tags.</summary>
[JsonPropertyName("tool_proficiencies")]
public string[] ToolProficiencies { get; init; } = Array.Empty<string>();
[JsonPropertyName("skills_choose")]
public int SkillsChoose { get; init; } = 0;
[JsonPropertyName("skill_options")]
public string[] SkillOptions { get; init; } = Array.Empty<string>();
/// <summary>
/// Per-level entries. Level 1..20. Phase 5 only consults level 1, but
/// the full table loads so the level-up flow doesn't need a schema bump.
/// </summary>
[JsonPropertyName("level_table")]
public ClassLevelEntry[] LevelTable { get; init; } = Array.Empty<ClassLevelEntry>();
/// <summary>Description of each named feature referenced from level_table.</summary>
[JsonPropertyName("feature_definitions")]
public Dictionary<string, ClassFeatureDef> FeatureDefinitions { get; init; } = new();
/// <summary>Allowed subclass ids (cross-reference into subclasses.json).</summary>
[JsonPropertyName("subclass_ids")]
public string[] SubclassIds { get; init; } = Array.Empty<string>();
/// <summary>
/// Items handed to a level-1 character of this class at creation time.
/// <see cref="Rules.Character.CharacterBuilder"/> adds each entry to the
/// inventory and, if <see cref="StartingKitItem.AutoEquip"/> is true,
/// equips it into <see cref="StartingKitItem.EquipSlot"/>.
/// </summary>
[JsonPropertyName("starting_kit")]
public StartingKitItem[] StartingKit { get; init; } = Array.Empty<StartingKitItem>();
}
/// <summary>
/// One row in <see cref="ClassDef.StartingKit"/>: the item id, quantity, and
/// optional auto-equip target. ItemId must resolve against items.json.
/// </summary>
public sealed record StartingKitItem
{
[JsonPropertyName("item_id")]
public string ItemId { get; init; } = "";
[JsonPropertyName("qty")]
public int Qty { get; init; } = 1;
/// <summary>If true, the item is equipped into <see cref="EquipSlot"/> at creation.</summary>
[JsonPropertyName("auto_equip")]
public bool AutoEquip { get; init; } = false;
/// <summary>"main_hand" / "off_hand" / "body" / "helm" / "cloak" / "boots" / "adaptive_pack" / etc.</summary>
[JsonPropertyName("equip_slot")]
public string EquipSlot { get; init; } = "";
}
public sealed record ClassLevelEntry
{
[JsonPropertyName("level")]
public int Level { get; init; } = 1;
[JsonPropertyName("prof")]
public int ProficiencyBonus { get; init; } = 2;
/// <summary>Feature ids unlocked at this level. Resolves into <see cref="ClassDef.FeatureDefinitions"/>.</summary>
[JsonPropertyName("features")]
public string[] Features { get; init; } = Array.Empty<string>();
}
public sealed record ClassFeatureDef
{
[JsonPropertyName("name")]
public string Name { get; init; } = "";
[JsonPropertyName("description")]
public string Description { get; init; } = "";
/// <summary>"passive", "active", "choice", "bonus_action", "reaction", "stub".</summary>
[JsonPropertyName("kind")]
public string Kind { get; init; } = "passive";
[JsonPropertyName("uses_per_short_rest")]
public int? UsesPerShortRest { get; init; }
[JsonPropertyName("uses_per_long_rest")]
public int? UsesPerLongRest { get; init; }
/// <summary>For "choice" features: the available pick ids.</summary>
[JsonPropertyName("options")]
public string[]? Options { get; init; }
}