using Godot; using Theriapolis.Core.Data; using Theriapolis.GodotHost.Scenes.Widgets; using Theriapolis.GodotHost.UI; namespace Theriapolis.GodotHost.Scenes.Steps; /// /// Step V — History. Direct port of StepBackground in /// src/steps.jsx: card per background showing flavor, granted /// skill / tool proficiencies, and a unique feature with a description /// popover. /// public partial class StepBackground : VBoxContainer, IStep { private CharacterDraft _draft = null!; private GridContainer _grid = null!; public void Bind(CharacterDraft draft) { _draft = draft; _draft.Changed += () => Callable.From(Refresh).CallDeferred(); Build(); } public string? Validate() => string.IsNullOrEmpty(_draft?.BackgroundId) ? "Pick a history." : null; private void Build() { AddThemeConstantOverride("separation", 16); var intro = new VBoxContainer(); intro.AddThemeConstantOverride("separation", 6); AddChild(intro); intro.AddChild(new Label { Text = "FOLIO V · HISTORY", ThemeTypeVariation = "Eyebrow" }); intro.AddChild(new Label { Text = "Choose a History", ThemeTypeVariation = "H2" }); intro.AddChild(new Label { Text = "Where your character came from before the wandering began. " + "History grants extra skill and tool proficiencies and a unique " + "feature that resolves in-fiction more often than in combat.", AutowrapMode = TextServer.AutowrapMode.WordSmart, }); _grid = new GridContainer { Columns = 3, SizeFlagsHorizontal = SizeFlags.ShrinkCenter }; _grid.AddThemeConstantOverride("h_separation", 16); _grid.AddThemeConstantOverride("v_separation", 16); AddChild(_grid); Refresh(); } private void Refresh() { if (_grid is null) return; foreach (var c in _grid.GetChildren()) c.QueueFree(); foreach (var bg in CodexContent.Backgrounds) { if (!BackgroundAvailability.IsAvailable(bg.Id, _draft)) continue; _grid.AddChild(BuildCard(bg)); } } private Control BuildCard(BackgroundDef bg) { bool selected = _draft.BackgroundId == bg.Id; var card = CodexCard.Make(); card.CustomMinimumSize = new Vector2(200, 0); CodexCard.SetSelected(card, selected); card.GuiInput += (InputEvent e) => { if (e is InputEventMouseButton mb && mb.Pressed && mb.ButtonIndex == MouseButton.Left) _draft.Patch(new Godot.Collections.Dictionary { { "background_id", bg.Id } }); }; var box = new VBoxContainer { MouseFilter = MouseFilterEnum.Pass }; box.AddThemeConstantOverride("separation", 6); card.AddChild(box); box.AddChild(new Label { Text = bg.Name, ThemeTypeVariation = "CardName" }); if (!string.IsNullOrEmpty(bg.Flavor)) { box.AddChild(new Label { Text = bg.Flavor, AutowrapMode = TextServer.AutowrapMode.WordSmart, ThemeTypeVariation = "CardBody", }); } // Skill + tool prof chips. if (bg.SkillProficiencies.Length > 0 || bg.ToolProficiencies.Length > 0) { var profs = new HFlowContainer { MouseFilter = MouseFilterEnum.Pass }; profs.AddThemeConstantOverride("h_separation", 6); profs.AddThemeConstantOverride("v_separation", 4); box.AddChild(profs); foreach (var s in bg.SkillProficiencies) profs.AddChild(new TraitChip { TraitName = s, Description = "Skill proficiency", Tag = "skill" }); foreach (var t in bg.ToolProficiencies) profs.AddChild(new TraitChip { TraitName = t, Description = "Tool proficiency", Tag = "tool" }); } // Background feature — chip whose popover shows the description. if (!string.IsNullOrEmpty(bg.FeatureName)) { var featRow = new HBoxContainer(); featRow.AddThemeConstantOverride("separation", 6); box.AddChild(featRow); featRow.AddChild(new Label { Text = "FEATURE", ThemeTypeVariation = "Eyebrow" }); featRow.AddChild(new TraitChip { TraitName = bg.FeatureName, Description = bg.FeatureDescription, }); } return card; } }