using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using UnityEngine; using UnityEngine.UI; using Vanguard.Api; using Vanguard.Api.Contracts; using Vanguard.Meta; using Vanguard.State; namespace Vanguard.UI { /// Коллекция игрока и разбор карт в пыль. public sealed class CollectionView : IDisposable { private readonly MetaService _meta; private readonly PlayerState _player; private readonly Action _onBack; private bool _disposed; private readonly RectTransform _root; private readonly Text _balances; private readonly Text _status; private readonly RectTransform _confirm; private Text _confirmText; private RectTransform _content; private CollectionItemDto _pending; private bool _loading; public CollectionView( Transform parent, MetaService meta, PlayerState player, Action onBack) { _meta = meta; _player = player; _onBack = onBack; _root = UiKit.CreatePanel(parent, "CollectionScreen", new Color(0.035f, 0.045f, 0.065f, 1f)); var top = UiKit.CreatePanel(_root, "TopBar", new Color(0.07f, 0.085f, 0.115f, 1f)); top.anchorMin = new Vector2(0f, 1f); top.anchorMax = new Vector2(1f, 1f); top.pivot = new Vector2(0.5f, 1f); top.anchoredPosition = Vector2.zero; top.sizeDelta = new Vector2(0f, 88f); UiKit.AddHorizontalLayout(top, 22, 16); var back = UiKit.CreateButton(top, "Back", "← Назад", () => _onBack(), 18); UiKit.SetPreferredHeight(back, 44f, 0f); var title = UiKit.CreateText(top, "Title", "Коллекция", 32, Color.white, TextAnchor.MiddleLeft); title.fontStyle = FontStyle.Bold; UiKit.SetPreferredHeight(title, 46f); _balances = UiKit.CreateText( top, "Balances", string.Empty, 18, new Color(0.70f, 0.76f, 0.90f), TextAnchor.MiddleRight); UiKit.SetPreferredHeight(_balances, 36f); var body = UiKit.CreatePanel(_root, "Body", new Color(0.035f, 0.045f, 0.065f, 0f)); body.anchorMin = Vector2.zero; body.anchorMax = Vector2.one; body.offsetMin = new Vector2(40f, 28f); body.offsetMax = new Vector2(-40f, -110f); UiKit.AddVerticalLayout(body, 8, 10); var hint = UiKit.CreateText( body, "Hint", "Разбор даёт пыль: C 5 · R 10 · RR 25 · RRR 100. SP не разбирается.", 17, new Color(0.58f, 0.65f, 0.78f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(hint, 26f); var scroll = UiKit.CreateScrollView(body, "CollectionScroll", out _content, 8f); var scrollElement = scroll.gameObject.AddComponent(); scrollElement.flexibleHeight = 1f; scrollElement.minHeight = 200f; _status = UiKit.CreateText( body, "Status", string.Empty, 18, new Color(0.62f, 0.68f, 0.82f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(_status, 34f); _confirm = BuildConfirm(parent); _confirm.gameObject.SetActive(false); _root.gameObject.SetActive(false); _player.Changed += OnPlayerChanged; } public void Show() { UiKit.SetActive(_root, true); _confirm.gameObject.SetActive(false); OnPlayerChanged(); _ = LoadAsync(); } public void Hide() { UiKit.SetActive(_root, false); _confirm.gameObject.SetActive(false); } public void Dispose() { if (_disposed) return; _disposed = true; _player.Changed -= OnPlayerChanged; } private RectTransform BuildConfirm(Transform parent) { var overlay = UiKit.CreatePanel(parent, "DismantleConfirm", new Color(0f, 0f, 0f, 0.72f)); var card = UiKit.CreateCenteredCard( overlay, "ConfirmCard", new Vector2(620f, 300f), new Color(0.08f, 0.10f, 0.14f, 1f)); UiKit.AddVerticalLayout(card, 30, 12); var title = UiKit.CreateText(card, "Title", "Разобрать карту?", 28, Color.white); title.fontStyle = FontStyle.Bold; UiKit.SetPreferredHeight(title, 42f); _confirmText = UiKit.CreateText( card, "Text", string.Empty, 19, new Color(0.72f, 0.78f, 0.92f), TextAnchor.MiddleCenter); UiKit.SetPreferredHeight(_confirmText, 110f); var yes = UiKit.CreateButton(card, "Yes", "Разобрать", OnConfirmDismantle, 20); UiKit.SetPreferredHeight(yes, 48f); var no = UiKit.CreateButton(card, "No", "Отмена", HideConfirm, 20); UiKit.SetPreferredHeight(no, 44f); overlay.gameObject.SetActive(false); return overlay; } private async Task LoadAsync() { if (_loading) return; _loading = true; SetStatus("Загружаем коллекцию…", new Color(0.62f, 0.68f, 0.82f)); try { var collection = await _meta.GetCollectionAsync(); if (_disposed) return; Rebuild(collection.Items ?? new List()); SetStatus( collection.Items == null || collection.Items.Count == 0 ? "Коллекция пуста. Пройдите онбординг." : "Карт в коллекции: " + collection.Items.Sum(item => item.Count) + ".", new Color(0.62f, 0.68f, 0.82f)); } catch (ApiException exception) { SetStatus(exception.Message, new Color(1f, 0.55f, 0.45f)); } catch (Exception exception) { Debug.LogException(exception); SetStatus("Не удалось загрузить коллекцию.", new Color(1f, 0.55f, 0.45f)); } finally { _loading = false; } } private void Rebuild(IReadOnlyList items) { for (var i = _content.childCount - 1; i >= 0; i--) UnityEngine.Object.Destroy(_content.GetChild(i).gameObject); if (items.Count == 0) return; foreach (var item in items) AddRow(item); } private void AddRow(CollectionItemDto item) { var row = UiKit.CreatePanel(_content, "Item_" + item.PrintingId, new Color(0.07f, 0.085f, 0.115f, 1f)); var rowElement = row.gameObject.AddComponent(); rowElement.preferredHeight = 64f; UiKit.AddHorizontalLayout(row, 10, 10); var name = UiKit.CreateText( row, "Name", item.NameEn, 19, Color.white, TextAnchor.MiddleLeft); var nameElement = name.gameObject.AddComponent(); nameElement.flexibleWidth = 1f; nameElement.preferredHeight = 40f; var printing = UiKit.CreateText( row, "Printing", item.PrintingId, 16, new Color(0.58f, 0.65f, 0.78f), TextAnchor.MiddleLeft); var printingElement = printing.gameObject.AddComponent(); printingElement.preferredWidth = 190f; printingElement.preferredHeight = 40f; var rarity = UiKit.CreateText( row, "Rarity", item.Rarity, 17, new Color(0.78f, 0.82f, 0.95f)); var rarityElement = rarity.gameObject.AddComponent(); rarityElement.preferredWidth = 64f; rarityElement.preferredHeight = 40f; var count = UiKit.CreateText( row, "Count", "×" + item.Count, 18, new Color(0.78f, 0.82f, 0.95f)); var countElement = count.gameObject.AddComponent(); countElement.preferredWidth = 54f; countElement.preferredHeight = 40f; var canDismantle = item.Rarity != "SP" && item.Count > 0; var dismantle = UiKit.CreateButton( row, "Dismantle", canDismantle ? "Разобрать 1" : "SP", () => ShowConfirm(item), 16); dismantle.interactable = canDismantle; var dismantleElement = dismantle.gameObject.AddComponent(); dismantleElement.preferredWidth = 150f; dismantleElement.preferredHeight = 44f; dismantleElement.flexibleWidth = 0f; } private void ShowConfirm(CollectionItemDto item) { _pending = item; _confirmText.text = "«" + item.NameEn + "» (" + item.PrintingId + ")\n" + "Вы получите " + DustFor(item.Rarity) + " пыли. Осталось: " + item.Count + "."; _confirm.SetAsLastSibling(); _confirm.gameObject.SetActive(true); } private void HideConfirm() { _pending = null; _confirm.gameObject.SetActive(false); } private async void OnConfirmDismantle() { if (_pending == null) return; var item = _pending; _confirm.gameObject.SetActive(false); SetStatus("Разбираем " + item.PrintingId + "…", new Color(0.62f, 0.68f, 0.82f)); try { await _meta.DismantleAsync(item.PrintingId, 1); SetStatus("Разобрано: " + item.PrintingId + ".", new Color(0.45f, 0.85f, 0.55f)); await LoadAsync(); } catch (ApiException exception) { SetStatus(exception.Message, new Color(1f, 0.55f, 0.45f)); } catch (Exception exception) { Debug.LogException(exception); SetStatus("Не удалось разобрать карту.", new Color(1f, 0.55f, 0.45f)); } finally { _pending = null; } } private static int DustFor(string rarity) { switch (rarity) { case "C": return 5; case "R": return 10; case "RR": return 25; case "RRR": return 100; default: return 0; } } private void OnPlayerChanged() { if (_disposed || _balances == null) return; _balances.text = "Gold " + _player.Gold + " · Пыль " + _player.Dust + " · Токены " + _player.Tokens; } private void SetStatus(string text, Color color) { if (_disposed || _status == null) return; _status.text = text ?? string.Empty; _status.color = color; } } }