Files
TheriapolisV3/Theriapolis.Core/Data/SpeciesDef.cs
T
Christopher Wiebe 66055f9549 M6.14: Single-column card layout with clade/species descriptions
Switch Step 0 (Clade) and Step 1 (Species) from a 3-column card grid
to a 1-column layout, with each card carrying a codex-voice
description paragraph between the meta line and the trait chips.
Rationale: establish the world's tone before mechanics — the player
reads who Canidae or Wolf-Folk *are* before evaluating ability mods
and trait pills. Trade is more vertical scrolling, but the card
content was already going wider than three columns comfortably
allowed once the parchment theme bumped padding.

Schema: CladeDef and SpeciesDef gain a Description field (string,
empty default). Populated for all 7 clades and 19 species, sourced
from the doc's italicized blockquote + a one-sentence summary of
the prose paragraph that follows. Empty descriptions fall through
silently — a species without a description still renders, just
without the paragraph.

UI: MakeGrid in both steps becomes Columns = 1 with ExpandFill;
BuildCard sets card.SizeFlagsHorizontal = ExpandFill (replaces the
fixed CustomMinimumSize 200) and prepends the autowrap description
label after the meta line. Hybrid mode stacks sire and dam single-
column grids vertically — same logic as before, just one card wide
each.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 21:47:00 -07:00

84 lines
3.1 KiB
C#

using System.Text.Json.Serialization;
namespace Theriapolis.Core.Data;
/// <summary>
/// Immutable species (subrace-equivalent) record loaded from species.json.
/// Refines the parent <see cref="CladeDef"/>: adds a body size, additional
/// ability mods, species-specific traits, and species-specific detriments.
/// </summary>
public sealed record SpeciesDef
{
[JsonPropertyName("id")]
public string Id { get; init; } = "";
[JsonPropertyName("clade_id")]
public string CladeId { get; init; } = "";
[JsonPropertyName("name")]
public string Name { get; init; } = "";
/// <summary>Codex-voice species description: usually the doc's italicized
/// blockquote followed by a one-sentence physical/cultural summary.
/// Surfaced on the Step 1 card.</summary>
[JsonPropertyName("description")]
public string Description { get; init; } = "";
/// <summary>Body size category, snake_case (small / medium / medium_large / large).</summary>
[JsonPropertyName("size")]
public string Size { get; init; } = "medium";
/// <summary>Additional ability mods on top of the clade's mods.</summary>
[JsonPropertyName("ability_mods")]
public Dictionary<string, int> AbilityMods { get; init; } = new();
/// <summary>Base movement speed in feet per turn (5 ft. = 1 tactical tile per d20 standard).</summary>
[JsonPropertyName("base_speed_ft")]
public int BaseSpeedFt { get; init; } = 30;
[JsonPropertyName("traits")]
public TraitDef[] Traits { get; init; } = Array.Empty<TraitDef>();
[JsonPropertyName("detriments")]
public TraitDef[] Detriments { get; init; } = Array.Empty<TraitDef>();
/// <summary>
/// "sex" (male/female) or "lineage" (e.g. sheep/goat for Ram-Folk).
/// Empty when the species has no variants. Determines how the variant
/// is resolved: sex-axis is auto-keyed off character Sex (purebred) or
/// parent role for hybrids (sire = male, dam = female); lineage-axis
/// requires an explicit per-species pick.
/// </summary>
[JsonPropertyName("variant_axis")]
public string VariantAxis { get; init; } = "";
/// <summary>
/// Variant entries layered onto the species' base traits/detriments.
/// The variant's id keys it: for sex-axis variants, "male" or "female";
/// for lineage variants, the lineage tag (e.g. "sheep", "goat").
/// Empty when the species has no variants.
/// </summary>
[JsonPropertyName("variants")]
public SpeciesVariantDef[] Variants { get; init; } = Array.Empty<SpeciesVariantDef>();
}
/// <summary>
/// One sex- or lineage-keyed variant of a species. Layered on top of the
/// base species' traits and detriments — the player ends up with both
/// sets, not one or the other.
/// </summary>
public sealed record SpeciesVariantDef
{
[JsonPropertyName("id")]
public string Id { get; init; } = "";
[JsonPropertyName("name")]
public string Name { get; init; } = "";
[JsonPropertyName("traits")]
public TraitDef[] Traits { get; init; } = Array.Empty<TraitDef>();
[JsonPropertyName("detriments")]
public TraitDef[] Detriments { get; init; } = Array.Empty<TraitDef>();
}