M6.8: Hybrid trait pickers (Phase B)
Per theriapolis-rpg-clades.md "Building a Hybrid": hybrids now pick two clade traits from the dominant parent + one from the other (2/1 split keyed off DominantParent), and one species trait + one species detriment from each parent. All clade detriments still inherit fully from both parents. Universal hybrid detriments unchanged. CharacterDraft gains six new fields (sire/dam clade-trait arrays, sire/dam species trait/detriment ids) and a CladeTraitLimit(lineage) helper. Step 0/1 validators enforce the picks; Aside renders only the chosen subset for hybrids. Cascading clears: clade swap clears that lineage's bonus + clade traits + (if species also invalidated) species pick; species swap clears that lineage's species trait/detriment; dominant flip trims overflow from the end (non-destructive when possible); hybrid-off clears all six new fields. Toggle buttons in both steps wire MouseEntered/Exited into PopoverLayer so the player can read each trait's description on hover (detriment buttons get the red-tinted "DETRIMENT" popover). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -249,24 +249,30 @@ public partial class Aside : MarginContainer
|
||||
flow.AddThemeConstantOverride("v_separation", 6);
|
||||
_content.AddChild(flow);
|
||||
|
||||
// Clade traits (purebred = single clade; hybrid = both).
|
||||
// Clade — purebred: full trait+detriment list of the one clade.
|
||||
// Hybrid: only the player-picked clade traits per parent (2/1 split),
|
||||
// but ALL clade detriments from BOTH parents per doc rule.
|
||||
if (_draft!.IsHybrid)
|
||||
{
|
||||
AddCladeTraits(flow, CodexContent.Clade(_draft.SireCladeId));
|
||||
AddCladeTraits(flow, CodexContent.Clade(_draft.DamCladeId));
|
||||
AddPickedCladeTraits(flow, CodexContent.Clade(_draft.SireCladeId), _draft.SireChosenCladeTraits);
|
||||
AddPickedCladeTraits(flow, CodexContent.Clade(_draft.DamCladeId), _draft.DamChosenCladeTraits);
|
||||
AddCladeDetriments(flow, CodexContent.Clade(_draft.SireCladeId));
|
||||
AddCladeDetriments(flow, CodexContent.Clade(_draft.DamCladeId));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddCladeTraits(flow, CodexContent.Clade(_draft.CladeId));
|
||||
}
|
||||
|
||||
// Species traits — purebred uses the single species; hybrids show
|
||||
// BOTH parent species per theriapolis-rpg-clades.md ("Choose ONE
|
||||
// species trait from each parent").
|
||||
// Species — purebred shows everything from the single species.
|
||||
// Hybrid shows only the picked trait + picked detriment per parent
|
||||
// (single-pick each, per doc) plus the four universal detriments.
|
||||
if (_draft.IsHybrid)
|
||||
{
|
||||
AddSpeciesTraits(flow, CodexContent.SpeciesById(_draft.SireSpeciesId));
|
||||
AddSpeciesTraits(flow, CodexContent.SpeciesById(_draft.DamSpeciesId));
|
||||
AddPickedSpeciesPick(flow, CodexContent.SpeciesById(_draft.SireSpeciesId),
|
||||
_draft.SireChosenSpeciesTrait, _draft.SireChosenSpeciesDetriment);
|
||||
AddPickedSpeciesPick(flow, CodexContent.SpeciesById(_draft.DamSpeciesId),
|
||||
_draft.DamChosenSpeciesTrait, _draft.DamChosenSpeciesDetriment);
|
||||
|
||||
// Universal hybrid detriments — every hybrid has all four.
|
||||
foreach (var (name, desc) in UniversalHybridDetriments)
|
||||
@@ -338,6 +344,37 @@ public partial class Aside : MarginContainer
|
||||
flow.AddChild(new TraitChip { TraitName = d.Name, Description = d.Description, Detriment = true });
|
||||
}
|
||||
|
||||
/// <summary>Hybrid: only the chosen subset of clade traits.</summary>
|
||||
private static void AddPickedCladeTraits(HFlowContainer flow, Theriapolis.Core.Data.CladeDef? clade,
|
||||
Godot.Collections.Array<string> chosen)
|
||||
{
|
||||
if (clade is null) return;
|
||||
foreach (var t in clade.Traits)
|
||||
if (chosen.Contains(t.Id))
|
||||
flow.AddChild(new TraitChip { TraitName = t.Name, Description = t.Description });
|
||||
}
|
||||
|
||||
/// <summary>Hybrid: all clade detriments from this parent (both inherited).</summary>
|
||||
private static void AddCladeDetriments(HFlowContainer flow, Theriapolis.Core.Data.CladeDef? clade)
|
||||
{
|
||||
if (clade is null) return;
|
||||
foreach (var d in clade.Detriments)
|
||||
flow.AddChild(new TraitChip { TraitName = d.Name, Description = d.Description, Detriment = true });
|
||||
}
|
||||
|
||||
/// <summary>Hybrid: one chosen species trait + one chosen species detriment.</summary>
|
||||
private static void AddPickedSpeciesPick(HFlowContainer flow, Theriapolis.Core.Data.SpeciesDef? species,
|
||||
string chosenTraitId, string chosenDetrimentId)
|
||||
{
|
||||
if (species is null) return;
|
||||
foreach (var t in species.Traits)
|
||||
if (t.Id == chosenTraitId)
|
||||
flow.AddChild(new TraitChip { TraitName = t.Name, Description = t.Description });
|
||||
foreach (var d in species.Detriments)
|
||||
if (d.Id == chosenDetrimentId)
|
||||
flow.AddChild(new TraitChip { TraitName = d.Name, Description = d.Description, Detriment = true });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hardcoded from theriapolis-rpg-clades.md "Universal Hybrid Detriments"
|
||||
/// (lines 749-753). Every hybrid character has all four. The schema
|
||||
|
||||
Reference in New Issue
Block a user