M6.16: Unified hybrid species grid + codex-styled hover popover
StepSpecies hybrid mode now uses one grid combining sire-clade species (under a "SIRE — <Clade>" eyebrow) and dam-clade species (under "DAM — <Clade>"). Cards are click-to-select like the purebred path — since clades are guaranteed disjoint by StepClade's parent-conflict rule, the lineage is implicit from the species' clade and no per-card toggles are needed. Hover popover now picks up the codex theme: parchment Bg2 panel with a gild border, rounded 14px corners, and soft drop shadow; H3 display serif title, mono Eyebrow tag, CardBody description. Detriment popovers swap to a 3px seal-red border via the panel_detriment stylebox override (replaces the old red Modulate hack). Theme propagation fix: CanvasLayer breaks Godot's Control theme inheritance, so the popup was rendering on Godot defaults. _Ready defers a lookup of the parent Control's theme and assigns it directly to the popup so the codex parchment + Cormorant/CrimsonPro fonts actually resolve. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -7,9 +7,11 @@ namespace Theriapolis.GodotHost.Scenes.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Step II — Species. Direct port of <c>StepSpecies</c> in
|
||||
/// <c>src/steps.jsx</c> plus the Phase 6.5 hybrid extension: when
|
||||
/// <see cref="CharacterDraft.IsHybrid"/> is true the step shows two
|
||||
/// stacked grids, one filtered by SireCladeId and one by DamCladeId.
|
||||
/// <c>src/steps.jsx</c> plus the Phase 6.5 hybrid extension. Hybrid mode
|
||||
/// uses a single unified grid (M6.16) — sire and dam species lists are
|
||||
/// merged, and each card carries either a Sire or Dam toggle in its
|
||||
/// header matching the clade it belongs to (sire/dam clades are
|
||||
/// guaranteed disjoint by StepClade's parent-conflict rule).
|
||||
/// </summary>
|
||||
public partial class StepSpecies : VBoxContainer, IStep
|
||||
{
|
||||
@@ -17,8 +19,7 @@ public partial class StepSpecies : VBoxContainer, IStep
|
||||
private VBoxContainer _purebredSection = null!;
|
||||
private VBoxContainer _hybridSection = null!;
|
||||
private GridContainer _purebredGrid = null!;
|
||||
private GridContainer _sireGrid = null!;
|
||||
private GridContainer _damGrid = null!;
|
||||
private GridContainer _hybridGrid = null!;
|
||||
|
||||
// Phase B species trait + detriment pickers — single-pick per lineage.
|
||||
private VBoxContainer _pickSection = null!;
|
||||
@@ -77,13 +78,14 @@ public partial class StepSpecies : VBoxContainer, IStep
|
||||
_hybridSection.AddThemeConstantOverride("separation", 16);
|
||||
AddChild(_hybridSection);
|
||||
|
||||
_hybridSection.AddChild(new Label { Text = "SIRE — Paternal Lineage", ThemeTypeVariation = "Eyebrow" });
|
||||
_sireGrid = MakeGrid();
|
||||
_hybridSection.AddChild(_sireGrid);
|
||||
|
||||
_hybridSection.AddChild(new Label { Text = "DAM — Maternal Lineage", ThemeTypeVariation = "Eyebrow" });
|
||||
_damGrid = MakeGrid();
|
||||
_hybridSection.AddChild(_damGrid);
|
||||
_hybridSection.AddChild(new Label
|
||||
{
|
||||
Text = "Pick one species per parent lineage. Sire's clade species "
|
||||
+ "are listed first, then Dam's — click any card to pick it.",
|
||||
AutowrapMode = TextServer.AutowrapMode.WordSmart,
|
||||
});
|
||||
_hybridGrid = MakeGrid();
|
||||
_hybridSection.AddChild(_hybridGrid);
|
||||
|
||||
// Phase B species pickers: one trait + one detriment per parent.
|
||||
_pickSection = new VBoxContainer();
|
||||
@@ -130,10 +132,7 @@ public partial class StepSpecies : VBoxContainer, IStep
|
||||
|
||||
if (hybrid)
|
||||
{
|
||||
RefreshGrid(_sireGrid, _draft.SireCladeId, _draft.SireSpeciesId,
|
||||
spId => OnLineageSpeciesPicked("sire", spId));
|
||||
RefreshGrid(_damGrid, _draft.DamCladeId, _draft.DamSpeciesId,
|
||||
spId => OnLineageSpeciesPicked("dam", spId));
|
||||
RefreshHybridGrid();
|
||||
|
||||
SyncSpeciesPicks(_sirePickCol, ref _sirePicksBuiltFor, "sire",
|
||||
_draft.SireSpeciesId, _draft.SireChosenSpeciesTrait, _draft.SireChosenSpeciesDetriment);
|
||||
@@ -398,6 +397,35 @@ public partial class StepSpecies : VBoxContainer, IStep
|
||||
grid.AddChild(BuildCard(sp, sp.Id == selectedSpecies, onClick));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hybrid mode: rebuild the unified grid with sire-clade species
|
||||
/// followed by dam-clade species. Sire and dam clades are
|
||||
/// guaranteed distinct by StepClade's parent-conflict rule, so the
|
||||
/// species lists are disjoint — each card unambiguously belongs to
|
||||
/// one lineage and a click on the card commits the pick. Full
|
||||
/// rebuild on every Refresh is safe because Bind installs Refresh
|
||||
/// as a deferred callback.
|
||||
/// </summary>
|
||||
private void RefreshHybridGrid()
|
||||
{
|
||||
foreach (var c in _hybridGrid.GetChildren()) c.Free();
|
||||
AddHybridLineageBlock("sire", _draft.SireCladeId, _draft.SireSpeciesId);
|
||||
AddHybridLineageBlock("dam", _draft.DamCladeId, _draft.DamSpeciesId);
|
||||
}
|
||||
|
||||
private void AddHybridLineageBlock(string lineage, string cladeId, string selectedSpeciesId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(cladeId)) return;
|
||||
var clade = CodexContent.Clade(cladeId);
|
||||
string headerLabel = (lineage == "sire" ? "SIRE" : "DAM")
|
||||
+ (clade is null ? "" : " — " + clade.Name.ToUpperInvariant());
|
||||
_hybridGrid.AddChild(new Label { Text = headerLabel, ThemeTypeVariation = "Eyebrow" });
|
||||
|
||||
foreach (var sp in CodexContent.SpeciesOfClade(cladeId))
|
||||
_hybridGrid.AddChild(BuildCard(sp, sp.Id == selectedSpeciesId,
|
||||
spId => OnLineageSpeciesPicked(lineage, spId)));
|
||||
}
|
||||
|
||||
private static Control BuildCard(SpeciesDef sp, bool selected, System.Action<string> onClick)
|
||||
{
|
||||
var card = CodexCard.Make();
|
||||
|
||||
Reference in New Issue
Block a user