using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using Vanguard.Api; using Vanguard.Api.Contracts; using Vanguard.Auth; using Vanguard.Battle; using Vanguard.Meta; namespace Vanguard.UI { /// /// Декбилдер в духе HS: коллекция в центре, узкая колонка колод/карт справа. /// Список колод и редактор используют один и тот же блок коллекции. /// public sealed class DeckBuilderView { private readonly MetaService _meta; private readonly AuthService _auth; private readonly Action _onBack; private readonly Action _onPlay; private readonly Action _onPracticeStarted; private readonly RectTransform _root; private readonly RectTransform _listView; private readonly RectTransform _editorView; private RectTransform _deckListContent; private RectTransform _listCollectionContent; private RectTransform _editorCollectionContent; private RectTransform _deckCardContent; private InputField _nameInput; private Text _listStatus; private Text _editorStatus; private Text _stats; private Text _deckCount; private Text _filterSummary; private RectTransform _filterOverlay; private RectTransform _clanContent; private RectTransform _confirmOverlay; private Text _confirmText; private DeckDto _pendingDelete; private RectTransform _cardOverlay; private Image _cardArt; private Text _cardArtName; private Text _cardTitle; private Text _cardSubtitle; private Text _cardStats; private Text _cardAbilities; private Text _cardOwned; private Text _cardStatus; private Button _dismantleButton; private Button _craftButton; private CollectionItemDto _selectedItem; private CardCatalogItemDto _selectedMeta; private readonly List _decks = new List(); private readonly List _collection = new List(); private readonly Dictionary _catalog = new Dictionary(StringComparer.Ordinal); private readonly List _cards = new List(); private readonly Dictionary _listCountTexts = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _editorCountTexts = new Dictionary(StringComparer.Ordinal); private DeckDto _editing; private int _dust; private string _filterClan = "Все"; private string _filterTrigger = "Все"; private int _filterGrade = -1; private bool _loading; private bool _busy; public DeckBuilderView( Transform parent, MetaService meta, AuthService auth, Action onBack, Action onPlay, Action onPracticeStarted) { _meta = meta; _auth = auth; _onBack = onBack; _onPlay = onPlay; _onPracticeStarted = onPracticeStarted; _root = UiKit.CreatePanel(parent, "DeckBuilderScreen", new Color(0.018f, 0.03f, 0.06f, 0.97f)); _listView = BuildListView(); _editorView = BuildEditorView(); _filterOverlay = BuildFilterOverlay(); _confirmOverlay = BuildDeleteConfirm(); _cardOverlay = BuildCardOverlay(); _listView.gameObject.SetActive(false); _editorView.gameObject.SetActive(false); _filterOverlay.gameObject.SetActive(false); _confirmOverlay.gameObject.SetActive(false); _cardOverlay.gameObject.SetActive(false); _root.gameObject.SetActive(false); } public void Show() { UiKit.SetActive(_root, true); ShowList(); _ = LoadAsync(); } public void Hide() { _filterOverlay.gameObject.SetActive(false); _confirmOverlay.gameObject.SetActive(false); _cardOverlay.gameObject.SetActive(false); _root.gameObject.SetActive(false); } private RectTransform BuildListView() { var view = UiKit.CreatePanel(_root, "DeckList", new Color(0.018f, 0.03f, 0.06f, 0.97f)); var top = BuildTopBar(view); var back = UiKit.CreateButton(top, "Back", "← В лобби", () => _onBack(), 17); SetFixedSize(back, 120f, 44f); var title = UiKit.CreateText(top, "Title", "Коллекция", 28, Color.white, TextAnchor.MiddleLeft); title.fontStyle = FontStyle.Bold; SetFixedSize(title, 150f, 44f); _filterSummary = UiKit.CreateText( top, "FilterSummary", "Все карты", 16, new Color(0.60f, 0.68f, 0.84f), TextAnchor.MiddleLeft); SetFlexible(_filterSummary, 1f, 44f); var filters = UiKit.CreateButton(top, "Filters", "Фильтры", OpenFilters, 17); SetFixedSize(filters, 110f, 44f); var reset = UiKit.CreateButton(top, "ResetFilters", "Сброс", ResetFilters, 17); SetFixedSize(reset, 90f, 44f); var create = UiKit.CreateButton(top, "Create", "Создать колоду", NewDeck, 17); SetFixedSize(create, 180f, 44f); var body = UiKit.CreatePanel(view, "Body", new Color(0f, 0f, 0f, 0f)); body.anchorMin = Vector2.zero; body.anchorMax = Vector2.one; body.offsetMin = new Vector2(34f, 28f); body.offsetMax = new Vector2(-34f, -108f); UiKit.AddHorizontalLayout(body, 0, 14); var collectionArea = UiKit.CreatePanel( body, "CollectionArea", new Color(0.04f, 0.06f, 0.10f, 0.55f)); var collectionElement = collectionArea.gameObject.AddComponent(); collectionElement.flexibleWidth = 1f; collectionElement.flexibleHeight = 1f; collectionElement.minHeight = 300f; UiKit.AddVerticalLayout(collectionArea, 8, 6); var scroll = UiKit.CreateGridScrollView( collectionArea, "CollectionGrid", new Vector2(174f, 238f), new Vector2(10f, 10f), out _listCollectionContent); var scrollElement = scroll.gameObject.AddComponent(); scrollElement.flexibleHeight = 1f; scrollElement.minHeight = 220f; _listStatus = UiKit.CreateText( collectionArea, "Status", string.Empty, 16, new Color(0.62f, 0.68f, 0.82f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(_listStatus, 32f); var deckArea = UiKit.CreatePanel( body, "DeckArea", new Color(0.04f, 0.06f, 0.10f, 0.85f)); var deckElement = deckArea.gameObject.AddComponent(); deckElement.preferredWidth = 300f; deckElement.minWidth = 280f; deckElement.flexibleWidth = 0f; deckElement.flexibleHeight = 1f; deckElement.minHeight = 300f; UiKit.AddVerticalLayout(deckArea, 10, 6); var deckTitle = UiKit.CreateText( deckArea, "Title", "Мои колоды", 20, Color.white, TextAnchor.MiddleLeft); deckTitle.fontStyle = FontStyle.Bold; UiKit.SetPreferredHeight(deckTitle, 30f); var deckScroll = UiKit.CreateScrollView(deckArea, "DeckListScroll", out _deckListContent, 8f); var deckScrollElement = deckScroll.gameObject.AddComponent(); deckScrollElement.flexibleHeight = 1f; deckScrollElement.minHeight = 200f; var hint = UiKit.CreateText( deckArea, "Hint", "Нажмите на колоду,\nчтобы открыть редактор.", 14, new Color(0.58f, 0.66f, 0.80f), TextAnchor.MiddleLeft); hint.horizontalOverflow = HorizontalWrapMode.Wrap; UiKit.SetPreferredHeight(hint, 46f); return view; } private RectTransform BuildEditorView() { var view = UiKit.CreatePanel(_root, "DeckEditor", new Color(0.018f, 0.03f, 0.06f, 0.97f)); var top = BuildTopBar(view); var back = UiKit.CreateButton(top, "Back", "← Коллекция", BackToList, 17); SetFixedSize(back, 120f, 44f); _nameInput = UiKit.CreateInput(top, "Name", "Название колоды"); SetFlexible(_nameInput, 0.7f, 46f); var filters = UiKit.CreateButton(top, "Filters", "Фильтры", OpenFilters, 17); SetFixedSize(filters, 110f, 44f); var reset = UiKit.CreateButton(top, "ResetFilters", "Сброс", ResetFilters, 17); SetFixedSize(reset, 90f, 44f); _stats = UiKit.CreateText( top, "Stats", string.Empty, 15, new Color(0.72f, 0.78f, 0.92f), TextAnchor.MiddleLeft); _stats.horizontalOverflow = HorizontalWrapMode.Wrap; SetFlexible(_stats, 1.4f, 50f); var save = UiKit.CreateButton(top, "Save", "Сохранить", SaveDeck, 17); SetFixedSize(save, 120f, 44f); var bot = UiKit.CreateButton(top, "Bot", "С ботом", PlayBot, 17); SetFixedSize(bot, 100f, 44f); var duel = UiKit.CreateButton(top, "Duel", "В бой", PlayDeck, 17); SetFixedSize(duel, 90f, 44f); var delete = UiKit.CreateButton(top, "Delete", "Удалить", RequestDeleteCurrent, 17); SetFixedSize(delete, 100f, 44f); var body = UiKit.CreatePanel(view, "Body", new Color(0f, 0f, 0f, 0f)); body.anchorMin = Vector2.zero; body.anchorMax = Vector2.one; body.offsetMin = new Vector2(34f, 28f); body.offsetMax = new Vector2(-34f, -108f); UiKit.AddHorizontalLayout(body, 0, 14); var collectionArea = UiKit.CreatePanel( body, "CollectionArea", new Color(0.04f, 0.06f, 0.10f, 0.55f)); var collectionElement = collectionArea.gameObject.AddComponent(); collectionElement.flexibleWidth = 1f; collectionElement.flexibleHeight = 1f; collectionElement.minHeight = 300f; UiKit.AddVerticalLayout(collectionArea, 8, 6); var scroll = UiKit.CreateGridScrollView( collectionArea, "CollectionGrid", new Vector2(174f, 238f), new Vector2(10f, 10f), out _editorCollectionContent); var scrollElement = scroll.gameObject.AddComponent(); scrollElement.flexibleHeight = 1f; scrollElement.minHeight = 220f; _editorStatus = UiKit.CreateText( collectionArea, "Status", string.Empty, 16, new Color(0.62f, 0.68f, 0.82f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(_editorStatus, 32f); var deckArea = UiKit.CreatePanel( body, "DeckArea", new Color(0.04f, 0.06f, 0.10f, 0.85f)); var deckElement = deckArea.gameObject.AddComponent(); deckElement.preferredWidth = 300f; deckElement.minWidth = 280f; deckElement.flexibleWidth = 0f; deckElement.flexibleHeight = 1f; deckElement.minHeight = 300f; UiKit.AddVerticalLayout(deckArea, 10, 6); _deckCount = UiKit.CreateText( deckArea, "DeckCount", "Колода: 0/50", 19, Color.white, TextAnchor.MiddleLeft); _deckCount.fontStyle = FontStyle.Bold; UiKit.SetPreferredHeight(_deckCount, 28f); var deckScroll = UiKit.CreateScrollView(deckArea, "DeckCardScroll", out _deckCardContent, 6f); var deckScrollElement = deckScroll.gameObject.AddComponent(); deckScrollElement.flexibleHeight = 1f; deckScrollElement.minHeight = 200f; var hint = UiKit.CreateText( deckArea, "Hint", "ЛКМ — убрать копию,\nПКМ — посмотреть карту.", 14, new Color(0.58f, 0.66f, 0.80f), TextAnchor.MiddleLeft); hint.horizontalOverflow = HorizontalWrapMode.Wrap; UiKit.SetPreferredHeight(hint, 46f); return view; } private RectTransform BuildTopBar(RectTransform view) { var top = UiKit.CreatePanel(view, "TopBar", new Color(0.06f, 0.08f, 0.13f, 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, 18, 12); return top; } private RectTransform BuildFilterOverlay() { var overlay = UiKit.CreatePanel(_root, "FilterOverlay", new Color(0f, 0f, 0f, 0.78f)); var card = UiKit.CreateCenteredCard( overlay, "FilterCard", new Vector2(860f, 820f), new Color(0.06f, 0.08f, 0.13f, 1f)); UiKit.AddVerticalLayout(card, 26, 10); var title = UiKit.CreateText(card, "Title", "Фильтры коллекции", 28, Color.white); title.fontStyle = FontStyle.Bold; UiKit.SetPreferredHeight(title, 40f); AddFilterSection(card, "Ранг", new[] { "Все", "0", "1", "2", "3" }, OnGradeFilter); AddFilterSection(card, "Триггер", new[] { "Все", "None", "Critical", "Draw", "Stand", "Heal" }, OnTriggerFilter); var clanTitle = UiKit.CreateText( card, "ClanTitle", "Клан", 17, new Color(0.66f, 0.74f, 0.90f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(clanTitle, 24f); var clanScroll = UiKit.CreateScrollView(card, "ClanScroll", out _clanContent, 4f); var clanElement = clanScroll.gameObject.AddComponent(); clanElement.flexibleHeight = 1f; clanElement.minHeight = 150f; var close = UiKit.CreateButton(card, "Close", "Готово", CloseFilters, 20); UiKit.SetPreferredHeight(close, 48f); return overlay; } private void RebuildClanFilters() { ClearChildren(_clanContent); AddClanButton(_clanContent, "Все"); foreach (var clan in _catalog.Values.Select(item => item.Clan).Distinct().OrderBy(item => item)) AddClanButton(_clanContent, clan); } private RectTransform BuildDeleteConfirm() { var overlay = UiKit.CreatePanel( _root, "DeleteDeckConfirm", new Color(0f, 0f, 0f, 0.78f)); var card = UiKit.CreateCenteredCard( overlay, "ConfirmCard", new Vector2(640f, 300f), new Color(0.06f, 0.08f, 0.13f, 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)); UiKit.SetPreferredHeight(_confirmText, 70f); var yes = UiKit.CreateButton(card, "Yes", "Удалить", ConfirmDelete, 20); UiKit.SetPreferredHeight(yes, 48f); var no = UiKit.CreateButton(card, "No", "Отмена", CancelDelete, 20); UiKit.SetPreferredHeight(no, 44f); return overlay; } private RectTransform BuildCardOverlay() { var overlay = UiKit.CreatePanel(_root, "CardOverlay", new Color(0f, 0f, 0f, 0.82f)); var card = UiKit.CreateCenteredCard( overlay, "CardView", new Vector2(1040f, 660f), new Color(0.05f, 0.07f, 0.11f, 1f)); var body = UiKit.CreatePanel(card, "Body", new Color(0f, 0f, 0f, 0f)); body.anchorMin = Vector2.zero; body.anchorMax = Vector2.one; body.offsetMin = new Vector2(26f, 26f); body.offsetMax = new Vector2(-26f, -26f); UiKit.AddHorizontalLayout(body, 0, 22); var art = UiKit.CreatePanel(body, "Art", new Color(0.2f, 0.3f, 0.5f, 1f)); SetFixedSize(art, 360f, 540f); _cardArt = art.GetComponent(); _cardArtName = UiKit.CreateText( art, "ArtName", string.Empty, 18, new Color(1f, 1f, 1f, 0.92f), TextAnchor.MiddleCenter); UiKit.Stretch(_cardArtName.rectTransform); _cardArtName.rectTransform.offsetMin = new Vector2(14f, 16f); _cardArtName.rectTransform.offsetMax = new Vector2(-14f, -16f); _cardArtName.horizontalOverflow = HorizontalWrapMode.Wrap; var info = UiKit.CreatePanel(body, "Info", new Color(0f, 0f, 0f, 0f)); var infoElement = info.gameObject.AddComponent(); infoElement.flexibleWidth = 1f; infoElement.flexibleHeight = 1f; UiKit.AddVerticalLayout(info, 4, 6); _cardTitle = UiKit.CreateText( info, "Title", string.Empty, 30, Color.white, TextAnchor.MiddleLeft); _cardTitle.fontStyle = FontStyle.Bold; _cardTitle.horizontalOverflow = HorizontalWrapMode.Wrap; UiKit.SetPreferredHeight(_cardTitle, 44f); _cardSubtitle = UiKit.CreateText( info, "Subtitle", string.Empty, 16, new Color(0.62f, 0.70f, 0.84f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(_cardSubtitle, 26f); _cardStats = UiKit.CreateText( info, "Stats", string.Empty, 15, new Color(0.72f, 0.78f, 0.92f), TextAnchor.UpperLeft); _cardStats.horizontalOverflow = HorizontalWrapMode.Wrap; UiKit.SetPreferredHeight(_cardStats, 88f); var abilitiesCaption = UiKit.CreateText( info, "AbilitiesCaption", "Способности", 16, new Color(0.66f, 0.74f, 0.90f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(abilitiesCaption, 24f); _cardAbilities = UiKit.CreateText( info, "Abilities", string.Empty, 15, new Color(0.82f, 0.86f, 0.96f), TextAnchor.UpperLeft); _cardAbilities.horizontalOverflow = HorizontalWrapMode.Wrap; var abilitiesElement = _cardAbilities.gameObject.AddComponent(); abilitiesElement.flexibleHeight = 1f; abilitiesElement.minHeight = 100f; _cardOwned = UiKit.CreateText( info, "Owned", string.Empty, 15, new Color(0.98f, 0.82f, 0.42f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(_cardOwned, 24f); _cardStatus = UiKit.CreateText( info, "Status", string.Empty, 14, new Color(0.62f, 0.68f, 0.82f), TextAnchor.UpperLeft); _cardStatus.horizontalOverflow = HorizontalWrapMode.Wrap; UiKit.SetPreferredHeight(_cardStatus, 38f); var actions = UiKit.CreatePanel(info, "Actions", new Color(0f, 0f, 0f, 0f)); UiKit.SetPreferredHeight(actions, 50f); UiKit.AddHorizontalLayout(actions, 0, 8); _dismantleButton = UiKit.CreateButton( actions, "Dismantle", "Разобрать", DismantleSelected, 16); SetFixedSize(_dismantleButton, 225f, 46f); _craftButton = UiKit.CreateButton( actions, "Craft", "Скрафтить", CraftSelected, 16); SetFixedSize(_craftButton, 225f, 46f); var close = UiKit.CreateButton(actions, "Close", "Закрыть", CloseCardOverlay, 16); SetFixedSize(close, 110f, 46f); return overlay; } private void AddRightClick(GameObject target, string printingId, CardCatalogItemDto meta) { var trigger = target.GetComponent() ?? target.AddComponent(); var entry = new EventTrigger.Entry { eventID = EventTriggerType.PointerClick }; entry.callback.AddListener(data => { if (data is PointerEventData pointer && pointer.button == PointerEventData.InputButton.Right) { ShowCardOverlay(printingId, meta); } }); trigger.triggers.Add(entry); } private void ShowCardOverlay(string printingId, CardCatalogItemDto meta) { if (meta == null) return; _selectedMeta = meta; _selectedItem = _collection.Find(item => item.PrintingId == printingId); _cardStatus.text = string.Empty; UpdateCardOverlay(); _cardOverlay.SetAsLastSibling(); _cardOverlay.gameObject.SetActive(true); } private void UpdateCardOverlay() { if (_selectedMeta == null) return; var meta = _selectedMeta; _cardArt.sprite = CardArt.ForCard(meta.NameId, meta.Clan); _cardArt.color = Color.white; _cardArt.preserveAspect = false; _cardArtName.text = meta.NameEn; _cardTitle.text = meta.NameEn; _cardSubtitle.text = (string.IsNullOrEmpty(meta.NameRu) ? string.Empty : meta.NameRu + " · ") + meta.Clan + " · G" + meta.Grade; _cardStats.text = "Тип: " + meta.Type + " · Редкость: " + meta.Rarity + "\n" + "Power " + meta.Power + " · Shield " + meta.Shield + " · Critical " + meta.Critical + "\n" + "Триггер: " + meta.Trigger + " · Sentinel: " + (meta.IsSentinel ? "да" : "нет") + " · Boost: " + (meta.HasBoost ? "да" : "нет") + " · Intercept: " + (meta.HasIntercept ? "да" : "нет") + "\n" + "Принт: " + meta.PrintingId; _cardAbilities.text = string.IsNullOrWhiteSpace(meta.TextRu) ? (string.IsNullOrWhiteSpace(meta.TextEn) ? "Текст способности отсутствует." : meta.TextEn) : meta.TextRu; _cardOwned.text = "В коллекции: ×" + (_selectedItem != null ? _selectedItem.Count : 0) + " · в колоде: " + UsedByName(meta.NameId) + " · пыль: " + _dust; var canChange = CanChange(meta.Rarity); var craftCost = CraftCostFor(meta.Rarity); _dismantleButton.interactable = canChange && !_busy && _selectedItem != null && _selectedItem.Count > 0; _craftButton.interactable = canChange && !_busy && _dust >= craftCost; SetButtonLabel( _dismantleButton, canChange ? "Разобрать (+" + DustFor(meta.Rarity) + " пыли)" : "Нельзя разобрать"); SetButtonLabel( _craftButton, canChange ? "Скрафтить (−" + craftCost + " пыли)" : "Нельзя скрафтить"); } private static bool CanChange(string rarity) => rarity == "C" || rarity == "R" || rarity == "RR" || rarity == "RRR"; private async void DismantleSelected() { if (_busy || _selectedItem == null || _selectedItem.Count <= 0 || _selectedMeta == null || !CanChange(_selectedMeta.Rarity)) return; var printingId = _selectedItem.PrintingId; _busy = true; UpdateCardOverlay(); _cardStatus.text = "Разбираем…"; _cardStatus.color = new Color(0.62f, 0.68f, 0.82f); try { await _auth.EnsureAccessAsync(); await _meta.DismantleAsync(printingId, 1); await ReloadCollectionAsync(printingId); SetCardStatus("Карта разобрана.", false); } catch (ApiException exception) { SetCardStatus(exception.Message, true); } catch (Exception exception) { Debug.LogException(exception); SetCardStatus("Не удалось разобрать карту.", true); } finally { _busy = false; UpdateCardOverlay(); } } private async void CraftSelected() { if (_busy || _selectedMeta == null || !CanChange(_selectedMeta.Rarity)) return; var printingId = _selectedMeta.PrintingId; _busy = true; UpdateCardOverlay(); _cardStatus.text = "Крафтим…"; _cardStatus.color = new Color(0.62f, 0.68f, 0.82f); try { await _auth.EnsureAccessAsync(); await _meta.CraftAsync(printingId); await ReloadCollectionAsync(printingId); SetCardStatus("Карта скрафчена.", false); } catch (ApiException exception) { SetCardStatus(exception.Message, true); } catch (Exception exception) { Debug.LogException(exception); SetCardStatus("Не удалось скрафтить карту.", true); } finally { _busy = false; UpdateCardOverlay(); } } private async Task ReloadCollectionAsync(string printingId) { var collection = await _meta.GetCollectionAsync(); _collection.Clear(); _collection.AddRange(collection.Items ?? new List()); _dust = collection.Balances != null ? collection.Balances.Dust : 0; RebuildCollectionGrids(); _selectedItem = _collection.Find(item => item.PrintingId == printingId); if (_selectedItem == null) { _cardOverlay.gameObject.SetActive(false); _listStatus.text = "Карт в коллекции: " + _collection.Count + "."; } else { UpdateCardOverlay(); } } private void SetCardStatus(string text, bool error) { _cardStatus.text = text; _cardStatus.color = error ? new Color(1f, 0.55f, 0.45f) : new Color(0.45f, 0.85f, 0.55f); } private void CloseCardOverlay() { _selectedItem = null; _selectedMeta = null; _cardOverlay.gameObject.SetActive(false); } private void AddFilterSection(Transform parent, string title, string[] labels, Action onSelected) { var caption = UiKit.CreateText( parent, "Caption_" + title, title, 17, new Color(0.66f, 0.74f, 0.90f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(caption, 24f); var row = UiKit.CreatePanel(parent, "Row_" + title, new Color(0f, 0f, 0f, 0f)); UiKit.SetPreferredHeight(row, 44f); UiKit.AddHorizontalLayout(row, 0, 6); foreach (var label in labels) { var captured = label; var button = UiKit.CreateButton( row, "Filter_" + label, label, () => onSelected(captured), 16); UiKit.SetPreferredHeight(button, 40f); } } private void AddClanButton(Transform parent, string clan) { var captured = clan; var button = UiKit.CreateButton( parent, "Clan_" + clan, clan, () => OnClanFilter(captured), 16); UiKit.SetPreferredHeight(button, 40f); } private async Task LoadAsync() { if (_loading) return; _loading = true; _listStatus.text = "Загружаем коллекцию и колоды…"; _listStatus.color = new Color(0.62f, 0.68f, 0.82f); ClearChildren(_deckListContent); try { await _auth.EnsureAccessAsync(); var collection = await _meta.GetCollectionAsync(); var catalog = await _meta.GetCatalogAsync(); var decks = await _meta.GetDecksAsync(); if (!_root.gameObject.activeSelf) return; _collection.Clear(); _collection.AddRange(collection.Items ?? new List()); _dust = collection.Balances != null ? collection.Balances.Dust : 0; _catalog.Clear(); foreach (var item in catalog) _catalog[item.PrintingId] = item; RebuildClanFilters(); _decks.Clear(); _decks.AddRange(decks); BuildDeckList(); RebuildCollectionGrids(); _listStatus.text = _decks.Count == 0 ? "Колод пока нет. Пыль: " + _dust + ". ПКМ по карте — подробности." : "Карт в коллекции: " + _collection.Count + " · пыль " + _dust + " · ПКМ по карте — подробности."; } catch (ApiException exception) { _listStatus.text = exception.Message; _listStatus.color = new Color(1f, 0.55f, 0.45f); } catch (Exception exception) { Debug.LogException(exception); _listStatus.text = "Не удалось загрузить декбилдер."; _listStatus.color = new Color(1f, 0.55f, 0.45f); } finally { _loading = false; } } private void BuildDeckList() { ClearChildren(_deckListContent); if (_decks.Count == 0) { var empty = UiKit.CreateText( _deckListContent, "Empty", "Нет колод", 16, new Color(0.58f, 0.66f, 0.80f)); UiKit.SetPreferredHeight(empty, 90f); return; } foreach (var deck in _decks) { var row = UiKit.CreatePanel( _deckListContent, "DeckRow_" + deck.Id, new Color(0.08f, 0.11f, 0.17f, 1f)); var rowElement = row.gameObject.AddComponent(); rowElement.preferredHeight = 76f; UiKit.AddVerticalLayout(row, 10, 2); var button = row.gameObject.AddComponent