using System; using System.Collections.Generic; using KernelPanic.Data; using KernelPanic.Meta; using UnityEngine; using UnityEngine.TextCore.Text; using UnityEngine.UIElements; namespace KernelPanic.UI { /// /// Renders banner selection and pull controls for the gacha screen. /// [Serializable] public sealed class GachaScreenController { private const string SelectedClassName = "selected"; private readonly List _bannerRows = new(); private readonly string[] _bannerIds = { GachaService.BeginnerBannerId, GachaService.StandardBannerId, GachaService.LimitedBannerId }; private VisualElement _bannerList; private VisualElement _bannerDetail; private DistroDatabase _distroDatabase; private FontAsset _monospaceFont; private GachaService _gachaService; private PlayerCollection _playerCollection; private EntropyWallet _wallet; private Func, PullResolutionResult> _resolvePulledDistros; private Action _onChanged; private Action _requestRootCreditExchange; private Action _requestPullCutscene; private int _selectedBannerIndex; private int _pendingPullCount; private int _pendingMissingTokens; private bool _hasOpened; private string _resultBannerId; private string _resultText; private readonly List _resultRewardLines = new(); private readonly List _resultRewardStars = new(); private bool _resultRevealed; public void Bind(VisualElement root, DistroDatabase database, FontAsset artFont, GachaService service, PlayerCollection collection, EntropyWallet entropyWallet, Func, PullResolutionResult> pullResolver, Action changedCallback, Action rootCreditExchangeCallback, Action pullCutsceneCallback) { _distroDatabase = database; _monospaceFont = artFont; _gachaService = service; _playerCollection = collection; _wallet = entropyWallet; _resolvePulledDistros = pullResolver; _onChanged = changedCallback; _requestRootCreditExchange = rootCreditExchangeCallback; _requestPullCutscene = pullCutsceneCallback; _bannerList = root.Q("GachaBannerList"); _bannerDetail = root.Q("GachaBannerDetail"); } public void Open() { if (!_hasOpened) { _selectedBannerIndex = 0; _hasOpened = true; } Refresh(); } public void Refresh() { if (_bannerList == null || _bannerDetail == null || _gachaService == null) { return; } _bannerRows.Clear(); _bannerList.Clear(); for (int i = 0; i < _bannerIds.Length; i++) { int index = i; VisualElement row = BuildBannerRow(_bannerIds[i]); row.RegisterCallback(_ => SelectBanner(index)); _bannerList.Add(row); _bannerRows.Add(row); } _selectedBannerIndex = Mathf.Clamp(_selectedBannerIndex, 0, _bannerRows.Count - 1); ApplySelectedBanner(); } public void HandleKeyDown(KeyDownEvent evt) { // Pulls are deliberately click-only to avoid accidental currency spend. } private VisualElement BuildBannerRow(string bannerId) { VisualElement row = new(); row.AddToClassList("gacha-banner-row"); row.Add(new Label(">") { name = $"GachaBannerCursor_{bannerId}" }); row.ElementAt(0).AddToClassList("command-cursor"); row.Add(new Label(GetBannerTitle(bannerId)) { name = $"GachaBannerName_{bannerId}" }); row.ElementAt(1).AddToClassList("command-text"); row.Add(new Label(GetBannerStatus(bannerId)) { name = $"GachaBannerStatus_{bannerId}" }); row.ElementAt(2).AddToClassList("command-description"); return row; } private void SelectBanner(int index) { if (_bannerRows.Count == 0) { return; } _selectedBannerIndex = (index + _bannerRows.Count) % _bannerRows.Count; ApplySelectedBanner(); } private void ApplySelectedBanner() { for (int i = 0; i < _bannerRows.Count; i++) { bool selected = i == _selectedBannerIndex; _bannerRows[i].EnableInClassList(SelectedClassName, selected); _bannerRows[i].Q