VanguardReMaster/client/Assets/Vanguard/Runtime/UI/DecksView.cs

357 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
/// <summary>Список колод и быстрый сборщик стартовой колоды из триал-деки.</summary>
public sealed class DecksView : IDisposable
{
private readonly MetaService _meta;
private readonly PlayerState _player;
private readonly Action _onBack;
private readonly Action<DeckDto> _onPlay;
private bool _disposed;
private readonly RectTransform _root;
private RectTransform _content;
private readonly Text _balances;
private readonly Text _status;
private readonly Button _createButton;
private readonly RectTransform _confirm;
private Text _confirmText;
private DeckDto _pendingDelete;
private bool _loading;
public DecksView(
Transform parent,
MetaService meta,
PlayerState player,
Action onBack,
Action<DeckDto> onPlay)
{
_meta = meta;
_player = player;
_onBack = onBack;
_onPlay = onPlay;
_root = UiKit.CreatePanel(parent, "DecksScreen", 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",
"Стартовая колода собирается из выбранной триал-деки: 50 карт, состав уже проверен сервером.",
17,
new Color(0.58f, 0.65f, 0.78f),
TextAnchor.MiddleLeft);
UiKit.SetPreferredHeight(hint, 26f);
var scroll = UiKit.CreateScrollView(body, "DecksScroll", out _content, 8f);
var scrollElement = scroll.gameObject.AddComponent<LayoutElement>();
scrollElement.flexibleHeight = 1f;
scrollElement.minHeight = 180f;
var actions = UiKit.CreatePanel(body, "Actions", new Color(0.05f, 0.06f, 0.085f, 0f));
UiKit.SetPreferredHeight(actions, 56f);
UiKit.AddHorizontalLayout(actions, 0, 12);
_createButton = UiKit.CreateButton(
actions,
"CreateFromTrial",
"Собрать из триал-деки",
OnCreateFromTrialClicked,
19);
UiKit.SetPreferredHeight(_createButton, 52f);
_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, "DeleteDeckConfirm", new Color(0f, 0f, 0f, 0.72f));
var card = UiKit.CreateCenteredCard(
overlay, "ConfirmCard", new Vector2(620f, 280f),
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, 80f);
var yes = UiKit.CreateButton(card, "Yes", "Удалить", OnConfirmDelete, 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 decks = await _meta.GetDecksAsync();
if (_disposed)
return;
Rebuild(decks);
SetStatus(
decks.Count == 0 ? "Колод пока нет." : "Колод: " + decks.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<DeckDto> decks)
{
for (var i = _content.childCount - 1; i >= 0; i--)
UnityEngine.Object.Destroy(_content.GetChild(i).gameObject);
foreach (var deck in decks)
AddRow(deck);
}
private void AddRow(DeckDto deck)
{
var row = UiKit.CreatePanel(_content, "Deck_" + deck.Id, new Color(0.07f, 0.085f, 0.115f, 1f));
var rowElement = row.gameObject.AddComponent<LayoutElement>();
rowElement.preferredHeight = 72f;
UiKit.AddHorizontalLayout(row, 12, 12);
var name = UiKit.CreateText(
row, "Name", deck.Name, 21, Color.white, TextAnchor.MiddleLeft);
name.fontStyle = FontStyle.Bold;
var nameElement = name.gameObject.AddComponent<LayoutElement>();
nameElement.flexibleWidth = 1f;
nameElement.preferredHeight = 44f;
var info = UiKit.CreateText(
row,
"Info",
deck.Cards.Count + " карт · " + deck.Format,
16,
new Color(0.58f, 0.65f, 0.78f),
TextAnchor.MiddleLeft);
var infoElement = info.gameObject.AddComponent<LayoutElement>();
infoElement.preferredWidth = 230f;
infoElement.preferredHeight = 40f;
var play = UiKit.CreateButton(row, "Play", "В бой", () => OnPlayClicked(deck), 17);
var playElement = play.gameObject.AddComponent<LayoutElement>();
playElement.preferredWidth = 120f;
playElement.preferredHeight = 46f;
playElement.flexibleWidth = 0f;
var delete = UiKit.CreateButton(row, "Delete", "Удалить", () => ShowConfirm(deck), 16);
var deleteElement = delete.gameObject.AddComponent<LayoutElement>();
deleteElement.preferredWidth = 110f;
deleteElement.preferredHeight = 46f;
deleteElement.flexibleWidth = 0f;
}
private async void OnCreateFromTrialClicked()
{
if (_loading)
return;
_loading = true;
_createButton.interactable = false;
SetStatus("Ищем выбранную триал-деку…", new Color(0.62f, 0.68f, 0.82f));
try
{
var store = await _meta.GetStoreAsync();
var trial = store.TrialDecks.FirstOrDefault(deck => deck.Owned);
if (trial == null)
{
SetStatus("Сначала завершите онбординг и выберите триал-деку.", new Color(1f, 0.60f, 0.45f));
return;
}
var cards = trial.Cards
.SelectMany(card => Enumerable.Repeat(card.PrintingId, card.Count))
.ToList();
var created = await _meta.CreateDeckAsync(trial.Name + " (старт)", cards);
SetStatus("Колода «" + created.Name + "» собрана: " + created.Cards.Count + " карт.", new Color(0.45f, 0.85f, 0.55f));
_loading = false;
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
{
_loading = false;
_createButton.interactable = true;
}
}
private void OnPlayClicked(DeckDto deck)
{
_onPlay(deck);
}
private void ShowConfirm(DeckDto deck)
{
_pendingDelete = deck;
_confirmText.text = "«" + deck.Name + "» будет удалена без возможности восстановления.";
_confirm.SetAsLastSibling();
_confirm.gameObject.SetActive(true);
}
private void HideConfirm()
{
_pendingDelete = null;
_confirm.gameObject.SetActive(false);
}
private async void OnConfirmDelete()
{
if (_pendingDelete == null)
return;
var deck = _pendingDelete;
_confirm.gameObject.SetActive(false);
SetStatus("Удаляем «" + deck.Name + "»…", new Color(0.62f, 0.68f, 0.82f));
try
{
await _meta.DeleteDeckAsync(deck.Id);
SetStatus("Колода удалена.", 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
{
_pendingDelete = null;
}
}
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;
}
}
}