using System; using System.Collections.Generic; using Cthangover.Core.Characters; using Cthangover.Core.Factories.Impls; using Cthangover.Core.Scenes; using Cthangover.Core.UI; using Cthangover.Mods.Interface.Base.Lists; using Cthangover.Core.Settings; using Cthangover.Core.Skills; using Cthangover.Core.UI.Theming; using Cthangover.Core.Utils; using Godot; using Cthangover.Mods.Interface.Theming; namespace Cthangover.Mods.Skills { /// /// Skills collection panel: the showcase grid of collected skill cards on /// the left, the study plate of the selected card on the right and the /// global "collection effect" plate hanging in the screen corner. The /// window structure lives in skills_panel.tscn; this behaviour only /// resolves the nodes, paints them with and fills /// them with data. /// /// The grid never reserves slots for cards the player does not own: the /// total card count in the world depends on the loaded mods and is unknown /// at runtime, so only owned skills are shown and the header counter /// reports the collected amount. The effect plate sums every owned skill's /// through , /// which is the "what the collection gives me right now" readout. /// /// Selecting a card only repaints the study plate; filters, search and sort /// rebuild the grid and keep the selection on the first visible card. All /// rarity colours come from , shared with the /// card scene, so a card and its plate can never disagree. /// public partial class SkillsPanelBehaviour : Widget { private enum SortMode { Rarity, Type, Name, } private const int Columns = 6; private const float CellGap = 10f; private const float SideWidth = 400f; private const float CardAspect = 210f / 160f; private readonly List _cards = new(); private ModScenePrefab _cardPrefab; private ColorRect _backdrop; private Panel _window; private Label _title; private HBoxContainer _tabsRow; private Label _progressLabel; private Label _progressValue; private Button _closeButton; private Panel _rule; private LineEdit _searchEdit; private OptionButton _sortOption; private HFlowContainer _rarityTabs; private ScrollContainer _gridScroll; private Control _gridContent; private Label _emptyLabel; private Label _hint; private Button _resetButton; private PanelContainer _side; private Label _sideLabel; private Control _plate; private Panel _plateBg; private PanelContainer _plateArt; private TextureRect _plateImage; private Label _plateGems; private PanelContainer _medal; private Label _medalGlyph; private PanelContainer _plateNameBox; private Label _plateName; private HFlowContainer _badges; private Label _desc; private HFlowContainer _chips; private PanelContainer _grant; private TextureRect _grantIcon; private Label _grantCaption; private Label _grantName; private Label _passportEmpty; private Panel _hud; private Panel _hudAccent; private Label _hudTitle; private Label _hudCount; private Panel _hudRule; private ScrollContainer _hudScroll; private GridContainer _hudList; private Label _hudEmpty; private Label _hudFoot; private SkillInfo _selected; private SkillType? _typeFilter; private RareType? _rarityFilter; private SortMode _sort = SortMode.Rarity; private string _search = string.Empty; /// Resolves nodes, applies styles and texts, loads the skill card prefab and wires search/sort/close handlers. public override void _Ready() { base._Ready(); ResolveNodes(); ApplyStaticStyles(); ApplyStaticTexts(); _cardPrefab = new ModScenePrefab("scenes/ui/skills_panel/skill_card.tscn"); if (_closeButton != null) _closeButton.Pressed += OnClosePressed; if (_searchEdit != null) _searchEdit.TextChanged += OnSearchChanged; if (_sortOption != null) _sortOption.ItemSelected += OnSortSelected; if (_resetButton != null) _resetButton.Pressed += OnResetPressed; GameLogger.Log("SKILLSPANEL", $"Ready: grid={_gridContent != null} side={_side != null} hud={_hudList != null} prefab={_cardPrefab != null}"); // A scene that starts visible never goes through Widget.Show (the // lifecycle only fires on the typed Show/Switch calls), so build the // panel here too — ShowConstruct rebuilds from scratch, and the next // open just repeats that rebuild. if (Visible) { EnsureConstructed(); ShowConstruct(); } } /// /// Rebuilds the whole panel on every open with a clean filter state, so /// the player always lands on the full collection. Newly collected /// skills appear here because the grid is rebuilt from /// rather than cached. /// protected override void ShowConstruct() { _typeFilter = null; _rarityFilter = null; _sort = SortMode.Rarity; _search = string.Empty; if (_searchEdit != null) _searchEdit.Text = string.Empty; if (_sortOption != null) _sortOption.Selected = 0; var owned = OwnedSkills(); RebuildTypeTabs(owned); RebuildRarityTabs(owned); RebuildGrid(owned); RebuildHud(owned); GameLogger.Log("SKILLSPANEL", $"ShowConstruct: {owned.Count} skill(s) owned"); } protected override void HideDestruct() { ClearCards(); _selected = null; ClearChildren(_badges); ClearChildren(_chips); } private void ResolveNodes() { const string root = "Window/Margin/Root"; _backdrop = GetNodeOrNull("Bg"); _window = GetNodeOrNull("Window"); _title = GetNodeOrNull