Merge collection into deck builder and add card overlay
CI / test (push) Successful in 8m48s
Details
CI / test (push) Successful in 8m48s
Details
This commit is contained in:
parent
14dce96218
commit
81af3aeb44
|
|
@ -227,14 +227,15 @@
|
||||||
главный экран профиля. Подробности — `client/README.md` и `docs/HANDOFF.md`.
|
главный экран профиля. Подробности — `client/README.md` и `docs/HANDOFF.md`.
|
||||||
|
|
||||||
**Визуальное направление зафиксировано:** 3D-клиент на URP, отдельные
|
**Визуальное направление зафиксировано:** 3D-клиент на URP, отдельные
|
||||||
сцены-экраны **Login / Lobby / Collection / DeckBuilder / Battle**;
|
сцены-экраны **Login / Lobby / DeckBuilder / Battle**;
|
||||||
`Boot` — служебная сцена. Очередь и онбординг — оверлеи Lobby. Текущий
|
`DeckBuilder` совмещает коллекцию и редактор колоды. `Boot` — служебная
|
||||||
|
сцена. Очередь и онбординг — оверлеи Lobby. Текущий
|
||||||
единый Canvas — временный функциональный gray-box. Полное решение —
|
единый Canvas — временный функциональный gray-box. Полное решение —
|
||||||
`docs/client-visual-direction.md`.
|
`docs/client-visual-direction.md`.
|
||||||
|
|
||||||
- [x] Unity 6 LTS, URP, Addressables, Input System, Localization.
|
- [x] Unity 6 LTS, URP, Addressables, Input System, Localization.
|
||||||
DOTween — следующим шагом (пока UI без внешних твинов).
|
DOTween — следующим шагом (пока UI без внешних твинов).
|
||||||
- [x] 3D-сцены `Login`, `Lobby`, `Collection`, `DeckBuilder`, `Battle`
|
- [x] 3D-сцены `Login`, `Lobby`, `DeckBuilder`, `Battle`
|
||||||
и `SceneFlow`; `Boot` — стартовая служебная сцена. 3D-окружение пока
|
и `SceneFlow`; `Boot` — стартовая служебная сцена. 3D-окружение пока
|
||||||
из примитивов, UI — code-generated uGUI.
|
из примитивов, UI — code-generated uGUI.
|
||||||
- [x] Composition root + DI (VContainer), без синглтон-свалок.
|
- [x] Composition root + DI (VContainer), без синглтон-свалок.
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ namespace Vanguard.Editor
|
||||||
|
|
||||||
Create<LoginSceneController>("Login");
|
Create<LoginSceneController>("Login");
|
||||||
Create<LobbySceneController>("Lobby");
|
Create<LobbySceneController>("Lobby");
|
||||||
Create<CollectionSceneController>("Collection");
|
|
||||||
Create<DeckBuilderSceneController>("DeckBuilder");
|
Create<DeckBuilderSceneController>("DeckBuilder");
|
||||||
Create<BattleSceneController>("Battle");
|
Create<BattleSceneController>("Battle");
|
||||||
|
|
||||||
|
|
@ -37,7 +36,6 @@ namespace Vanguard.Editor
|
||||||
Add(scenes, BootPath);
|
Add(scenes, BootPath);
|
||||||
Add(scenes, SceneDir + "/Login.unity");
|
Add(scenes, SceneDir + "/Login.unity");
|
||||||
Add(scenes, SceneDir + "/Lobby.unity");
|
Add(scenes, SceneDir + "/Lobby.unity");
|
||||||
Add(scenes, SceneDir + "/Collection.unity");
|
|
||||||
Add(scenes, SceneDir + "/DeckBuilder.unity");
|
Add(scenes, SceneDir + "/DeckBuilder.unity");
|
||||||
Add(scenes, SceneDir + "/Battle.unity");
|
Add(scenes, SceneDir + "/Battle.unity");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,12 @@ namespace Vanguard.Api.Contracts
|
||||||
[JsonProperty("idempotencyKey")] public string IdempotencyKey { get; set; } = string.Empty;
|
[JsonProperty("idempotencyKey")] public string IdempotencyKey { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class CraftRequest
|
||||||
|
{
|
||||||
|
[JsonProperty("printingId")] public string PrintingId { get; set; } = string.Empty;
|
||||||
|
[JsonProperty("idempotencyKey")] public string IdempotencyKey { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class DeckDto
|
public sealed class DeckDto
|
||||||
{
|
{
|
||||||
[JsonProperty("id")] public Guid Id { get; set; }
|
[JsonProperty("id")] public Guid Id { get; set; }
|
||||||
|
|
@ -277,12 +283,22 @@ namespace Vanguard.Api.Contracts
|
||||||
[JsonProperty("printingId")] public string PrintingId { get; set; } = string.Empty;
|
[JsonProperty("printingId")] public string PrintingId { get; set; } = string.Empty;
|
||||||
[JsonProperty("nameId")] public string NameId { get; set; } = string.Empty;
|
[JsonProperty("nameId")] public string NameId { get; set; } = string.Empty;
|
||||||
[JsonProperty("nameEn")] public string NameEn { get; set; } = string.Empty;
|
[JsonProperty("nameEn")] public string NameEn { get; set; } = string.Empty;
|
||||||
|
[JsonProperty("nameRu")] public string NameRu { get; set; }
|
||||||
[JsonProperty("rarity")] public string Rarity { get; set; } = string.Empty;
|
[JsonProperty("rarity")] public string Rarity { get; set; } = string.Empty;
|
||||||
[JsonProperty("grade")] public int Grade { get; set; }
|
[JsonProperty("grade")] public int Grade { get; set; }
|
||||||
|
[JsonProperty("type")] public string Type { get; set; } = string.Empty;
|
||||||
|
[JsonProperty("power")] public int Power { get; set; }
|
||||||
|
[JsonProperty("shield")] public int Shield { get; set; }
|
||||||
|
[JsonProperty("critical")] public int Critical { get; set; }
|
||||||
[JsonProperty("trigger")] public string Trigger { get; set; } = "None";
|
[JsonProperty("trigger")] public string Trigger { get; set; } = "None";
|
||||||
[JsonProperty("clan")] public string Clan { get; set; } = string.Empty;
|
[JsonProperty("clan")] public string Clan { get; set; } = string.Empty;
|
||||||
[JsonProperty("isSentinel")] public bool IsSentinel { get; set; }
|
[JsonProperty("isSentinel")] public bool IsSentinel { get; set; }
|
||||||
|
[JsonProperty("isRestraint")] public bool IsRestraint { get; set; }
|
||||||
|
[JsonProperty("hasBoost")] public bool HasBoost { get; set; }
|
||||||
|
[JsonProperty("hasIntercept")] public bool HasIntercept { get; set; }
|
||||||
[JsonProperty("isParallel")] public bool IsParallel { get; set; }
|
[JsonProperty("isParallel")] public bool IsParallel { get; set; }
|
||||||
[JsonProperty("basePrintingId")] public string BasePrintingId { get; set; }
|
[JsonProperty("basePrintingId")] public string BasePrintingId { get; set; }
|
||||||
|
[JsonProperty("textEn")] public string TextEn { get; set; }
|
||||||
|
[JsonProperty("textRu")] public string TextRu { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
using Vanguard.Meta;
|
|
||||||
using Vanguard.State;
|
|
||||||
using Vanguard.UI;
|
|
||||||
|
|
||||||
namespace Vanguard.App
|
|
||||||
{
|
|
||||||
public sealed class CollectionSceneController : SceneControllerBase
|
|
||||||
{
|
|
||||||
private CollectionView _view;
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
SceneEnvironment.Build(SceneTheme.Collection);
|
|
||||||
var canvas = CreateCanvas("Collection Canvas");
|
|
||||||
_view = new CollectionView(
|
|
||||||
canvas.transform,
|
|
||||||
Resolve<MetaService>(),
|
|
||||||
Resolve<PlayerState>(),
|
|
||||||
() => _ = Flow.LoadLobbyAsync());
|
|
||||||
_view.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
|
||||||
{
|
|
||||||
_view?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 8ccb09cec5c89814493fae05cf2d6051
|
|
||||||
|
|
@ -33,7 +33,6 @@ namespace Vanguard.App
|
||||||
_auth,
|
_auth,
|
||||||
_player,
|
_player,
|
||||||
OnLogout,
|
OnLogout,
|
||||||
() => _ = Flow.LoadCollectionAsync(),
|
|
||||||
() => _ = Flow.LoadDeckBuilderAsync(),
|
() => _ = Flow.LoadDeckBuilderAsync(),
|
||||||
ShowStore,
|
ShowStore,
|
||||||
ShowHistory,
|
ShowHistory,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ namespace Vanguard.App
|
||||||
{
|
{
|
||||||
Login,
|
Login,
|
||||||
Lobby,
|
Lobby,
|
||||||
Collection,
|
|
||||||
DeckBuilder,
|
DeckBuilder,
|
||||||
Battle,
|
Battle,
|
||||||
}
|
}
|
||||||
|
|
@ -143,15 +142,6 @@ namespace Vanguard.App
|
||||||
Cube(root, "Lobby Light", new Vector3(0f, 5.2f, 4f), new Vector3(6f, 0.2f, 1f), palette.Accent, 1.8f);
|
Cube(root, "Lobby Light", new Vector3(0f, 5.2f, 4f), new Vector3(6f, 0.2f, 1f), palette.Accent, 1.8f);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SceneTheme.Collection:
|
|
||||||
for (var i = -2; i <= 2; i++)
|
|
||||||
{
|
|
||||||
Cube(root, "Shelf " + i, new Vector3(i * 3.2f, 1.6f, 6.5f), new Vector3(2.2f, 3.2f, 0.5f), palette.Structure);
|
|
||||||
Cube(root, "Shelf Glow " + i, new Vector3(i * 3.2f, 3.35f, 6.2f), new Vector3(2.2f, 0.1f, 0.5f), palette.Accent, 1.2f);
|
|
||||||
}
|
|
||||||
Cube(root, "Table", new Vector3(0f, 0.4f, 1.5f), new Vector3(7f, 0.8f, 4f), palette.Structure);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SceneTheme.DeckBuilder:
|
case SceneTheme.DeckBuilder:
|
||||||
Cube(root, "Table", new Vector3(0f, 0.5f, 2f), new Vector3(11f, 1f, 6f), palette.Structure);
|
Cube(root, "Table", new Vector3(0f, 0.5f, 2f), new Vector3(11f, 1f, 6f), palette.Structure);
|
||||||
Cube(root, "Deck Stack", new Vector3(-3.8f, 1.3f, 3.2f), new Vector3(1.5f, 1.4f, 2.2f), palette.Accent, 0.8f);
|
Cube(root, "Deck Stack", new Vector3(-3.8f, 1.3f, 3.2f), new Vector3(1.5f, 1.4f, 2.2f), palette.Accent, 0.8f);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ namespace Vanguard.App
|
||||||
public const string Boot = "Boot";
|
public const string Boot = "Boot";
|
||||||
public const string Login = "Login";
|
public const string Login = "Login";
|
||||||
public const string Lobby = "Lobby";
|
public const string Lobby = "Lobby";
|
||||||
public const string Collection = "Collection";
|
|
||||||
public const string DeckBuilder = "DeckBuilder";
|
public const string DeckBuilder = "DeckBuilder";
|
||||||
public const string Battle = "Battle";
|
public const string Battle = "Battle";
|
||||||
|
|
||||||
|
|
@ -32,8 +31,6 @@ namespace Vanguard.App
|
||||||
|
|
||||||
public Task LoadLobbyAsync() => LoadAsync(Lobby);
|
public Task LoadLobbyAsync() => LoadAsync(Lobby);
|
||||||
|
|
||||||
public Task LoadCollectionAsync() => LoadAsync(Collection);
|
|
||||||
|
|
||||||
public Task LoadDeckBuilderAsync() => LoadAsync(DeckBuilder);
|
public Task LoadDeckBuilderAsync() => LoadAsync(DeckBuilder);
|
||||||
|
|
||||||
public Task OpenLobbyWithQueueAsync(DeckDto deck)
|
public Task OpenLobbyWithQueueAsync(DeckDto deck)
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,20 @@ namespace Vanguard.Meta
|
||||||
return balances;
|
return balances;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Balances> CraftAsync(string printingId)
|
||||||
|
{
|
||||||
|
var balances = await _api.PostAsync<Balances>(
|
||||||
|
"/api/collection/craft",
|
||||||
|
new CraftRequest
|
||||||
|
{
|
||||||
|
PrintingId = printingId,
|
||||||
|
IdempotencyKey = Guid.NewGuid().ToString("N"),
|
||||||
|
});
|
||||||
|
|
||||||
|
_player.ApplyBalances(balances);
|
||||||
|
return balances;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<DeckDto>> GetDecksAsync()
|
public async Task<List<DeckDto>> GetDecksAsync()
|
||||||
{
|
{
|
||||||
var decks = await _api.GetAsync<List<DeckDto>>("/api/decks");
|
var decks = await _api.GetAsync<List<DeckDto>>("/api/decks");
|
||||||
|
|
|
||||||
|
|
@ -1,318 +0,0 @@
|
||||||
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 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<LayoutElement>();
|
|
||||||
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<CollectionItemDto>());
|
|
||||||
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<CollectionItemDto> 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<LayoutElement>();
|
|
||||||
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<LayoutElement>();
|
|
||||||
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<LayoutElement>();
|
|
||||||
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<LayoutElement>();
|
|
||||||
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<LayoutElement>();
|
|
||||||
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<LayoutElement>();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 117359b61c1e3454d8dcaebc08bfa85f
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using Vanguard.Api;
|
using Vanguard.Api;
|
||||||
using Vanguard.Api.Contracts;
|
using Vanguard.Api.Contracts;
|
||||||
|
|
@ -41,6 +42,20 @@ namespace Vanguard.UI
|
||||||
private Text _confirmText;
|
private Text _confirmText;
|
||||||
private DeckDto _pendingDelete;
|
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<DeckDto> _decks = new List<DeckDto>();
|
private readonly List<DeckDto> _decks = new List<DeckDto>();
|
||||||
private readonly List<CollectionItemDto> _collection = new List<CollectionItemDto>();
|
private readonly List<CollectionItemDto> _collection = new List<CollectionItemDto>();
|
||||||
private readonly Dictionary<string, CardCatalogItemDto> _catalog =
|
private readonly Dictionary<string, CardCatalogItemDto> _catalog =
|
||||||
|
|
@ -75,11 +90,13 @@ namespace Vanguard.UI
|
||||||
_editorView = BuildEditorView();
|
_editorView = BuildEditorView();
|
||||||
_filterOverlay = BuildFilterOverlay();
|
_filterOverlay = BuildFilterOverlay();
|
||||||
_confirmOverlay = BuildDeleteConfirm();
|
_confirmOverlay = BuildDeleteConfirm();
|
||||||
|
_cardOverlay = BuildCardOverlay();
|
||||||
|
|
||||||
_listView.gameObject.SetActive(false);
|
_listView.gameObject.SetActive(false);
|
||||||
_editorView.gameObject.SetActive(false);
|
_editorView.gameObject.SetActive(false);
|
||||||
_filterOverlay.gameObject.SetActive(false);
|
_filterOverlay.gameObject.SetActive(false);
|
||||||
_confirmOverlay.gameObject.SetActive(false);
|
_confirmOverlay.gameObject.SetActive(false);
|
||||||
|
_cardOverlay.gameObject.SetActive(false);
|
||||||
_root.gameObject.SetActive(false);
|
_root.gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +111,7 @@ namespace Vanguard.UI
|
||||||
{
|
{
|
||||||
_filterOverlay.gameObject.SetActive(false);
|
_filterOverlay.gameObject.SetActive(false);
|
||||||
_confirmOverlay.gameObject.SetActive(false);
|
_confirmOverlay.gameObject.SetActive(false);
|
||||||
|
_cardOverlay.gameObject.SetActive(false);
|
||||||
_root.gameObject.SetActive(false);
|
_root.gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,7 +123,7 @@ namespace Vanguard.UI
|
||||||
var back = UiKit.CreateButton(top, "Back", "← В лобби", () => _onBack(), 17);
|
var back = UiKit.CreateButton(top, "Back", "← В лобби", () => _onBack(), 17);
|
||||||
SetFixedSize(back, 120f, 44f);
|
SetFixedSize(back, 120f, 44f);
|
||||||
|
|
||||||
var title = UiKit.CreateText(top, "Title", "Колоды", 28, Color.white, TextAnchor.MiddleLeft);
|
var title = UiKit.CreateText(top, "Title", "Коллекция", 28, Color.white, TextAnchor.MiddleLeft);
|
||||||
title.fontStyle = FontStyle.Bold;
|
title.fontStyle = FontStyle.Bold;
|
||||||
SetFixedSize(title, 150f, 44f);
|
SetFixedSize(title, 150f, 44f);
|
||||||
|
|
||||||
|
|
@ -187,7 +205,7 @@ namespace Vanguard.UI
|
||||||
var view = UiKit.CreatePanel(_root, "DeckEditor", new Color(0.018f, 0.03f, 0.06f, 0.97f));
|
var view = UiKit.CreatePanel(_root, "DeckEditor", new Color(0.018f, 0.03f, 0.06f, 0.97f));
|
||||||
|
|
||||||
var top = BuildTopBar(view);
|
var top = BuildTopBar(view);
|
||||||
var back = UiKit.CreateButton(top, "Back", "← Колоды", BackToList, 17);
|
var back = UiKit.CreateButton(top, "Back", "← Коллекция", BackToList, 17);
|
||||||
SetFixedSize(back, 120f, 44f);
|
SetFixedSize(back, 120f, 44f);
|
||||||
|
|
||||||
_nameInput = UiKit.CreateInput(top, "Name", "Название колоды");
|
_nameInput = UiKit.CreateInput(top, "Name", "Название колоды");
|
||||||
|
|
@ -265,7 +283,7 @@ namespace Vanguard.UI
|
||||||
deckScrollElement.minHeight = 200f;
|
deckScrollElement.minHeight = 200f;
|
||||||
|
|
||||||
var hint = UiKit.CreateText(
|
var hint = UiKit.CreateText(
|
||||||
deckArea, "Hint", "Нажмите на карту,\nчтобы убрать одну копию.", 14,
|
deckArea, "Hint", "ЛКМ — убрать копию,\nПКМ — посмотреть карту.", 14,
|
||||||
new Color(0.58f, 0.66f, 0.80f), TextAnchor.MiddleLeft);
|
new Color(0.58f, 0.66f, 0.80f), TextAnchor.MiddleLeft);
|
||||||
hint.horizontalOverflow = HorizontalWrapMode.Wrap;
|
hint.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||||
UiKit.SetPreferredHeight(hint, 46f);
|
UiKit.SetPreferredHeight(hint, 46f);
|
||||||
|
|
@ -352,6 +370,265 @@ namespace Vanguard.UI
|
||||||
return overlay;
|
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<Image>();
|
||||||
|
|
||||||
|
_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<LayoutElement>();
|
||||||
|
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<LayoutElement>();
|
||||||
|
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<EventTrigger>()
|
||||||
|
?? target.AddComponent<EventTrigger>();
|
||||||
|
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.color = ClanColor(meta.Clan);
|
||||||
|
_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);
|
||||||
|
|
||||||
|
var canChange = CanChange(meta.Rarity);
|
||||||
|
_dismantleButton.interactable = canChange && !_busy
|
||||||
|
&& _selectedItem != null && _selectedItem.Count > 0;
|
||||||
|
_craftButton.interactable = canChange && !_busy;
|
||||||
|
SetButtonLabel(
|
||||||
|
_dismantleButton,
|
||||||
|
canChange ? "Разобрать (+" + DustFor(meta.Rarity) + " пыли)" : "Нельзя разобрать");
|
||||||
|
SetButtonLabel(
|
||||||
|
_craftButton,
|
||||||
|
canChange ? "Скрафтить (−" + CraftCostFor(meta.Rarity) + " пыли)" : "Нельзя скрафтить");
|
||||||
|
}
|
||||||
|
|
||||||
|
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<CollectionItemDto>());
|
||||||
|
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<string> onSelected)
|
private void AddFilterSection(Transform parent, string title, string[] labels, Action<string> onSelected)
|
||||||
{
|
{
|
||||||
var caption = UiKit.CreateText(
|
var caption = UiKit.CreateText(
|
||||||
|
|
@ -411,8 +688,8 @@ namespace Vanguard.UI
|
||||||
BuildDeckList();
|
BuildDeckList();
|
||||||
RebuildCollectionGrids();
|
RebuildCollectionGrids();
|
||||||
_listStatus.text = _decks.Count == 0
|
_listStatus.text = _decks.Count == 0
|
||||||
? "Колод пока нет. Создайте первую."
|
? "Колод пока нет. Создайте первую. ПКМ по карте — подробности."
|
||||||
: "Карт в коллекции: " + _collection.Count + ".";
|
: "Карт в коллекции: " + _collection.Count + " · ПКМ по карте — подробности.";
|
||||||
}
|
}
|
||||||
catch (ApiException exception)
|
catch (ApiException exception)
|
||||||
{
|
{
|
||||||
|
|
@ -491,6 +768,7 @@ namespace Vanguard.UI
|
||||||
_listView.gameObject.SetActive(false);
|
_listView.gameObject.SetActive(false);
|
||||||
_editorView.gameObject.SetActive(true);
|
_editorView.gameObject.SetActive(true);
|
||||||
_filterOverlay.gameObject.SetActive(false);
|
_filterOverlay.gameObject.SetActive(false);
|
||||||
|
_cardOverlay.gameObject.SetActive(false);
|
||||||
_editorStatus.text = string.Empty;
|
_editorStatus.text = string.Empty;
|
||||||
RebuildEditor();
|
RebuildEditor();
|
||||||
}
|
}
|
||||||
|
|
@ -498,6 +776,7 @@ namespace Vanguard.UI
|
||||||
private void BackToList()
|
private void BackToList()
|
||||||
{
|
{
|
||||||
_filterOverlay.gameObject.SetActive(false);
|
_filterOverlay.gameObject.SetActive(false);
|
||||||
|
_cardOverlay.gameObject.SetActive(false);
|
||||||
_editorView.gameObject.SetActive(false);
|
_editorView.gameObject.SetActive(false);
|
||||||
_listView.gameObject.SetActive(true);
|
_listView.gameObject.SetActive(true);
|
||||||
RefreshAllCounts();
|
RefreshAllCounts();
|
||||||
|
|
@ -508,6 +787,7 @@ namespace Vanguard.UI
|
||||||
_editorView.gameObject.SetActive(false);
|
_editorView.gameObject.SetActive(false);
|
||||||
_filterOverlay.gameObject.SetActive(false);
|
_filterOverlay.gameObject.SetActive(false);
|
||||||
_confirmOverlay.gameObject.SetActive(false);
|
_confirmOverlay.gameObject.SetActive(false);
|
||||||
|
_cardOverlay.gameObject.SetActive(false);
|
||||||
_listView.gameObject.SetActive(true);
|
_listView.gameObject.SetActive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -566,6 +846,7 @@ namespace Vanguard.UI
|
||||||
var cell = UiKit.CreatePanel(
|
var cell = UiKit.CreatePanel(
|
||||||
content, "Owned_" + item.PrintingId, new Color(0.08f, 0.11f, 0.17f, 1f));
|
content, "Owned_" + item.PrintingId, new Color(0.08f, 0.11f, 0.17f, 1f));
|
||||||
UiKit.AddVerticalLayout(cell, 6, 2);
|
UiKit.AddVerticalLayout(cell, 6, 2);
|
||||||
|
AddRightClick(cell.gameObject, item.PrintingId, meta);
|
||||||
|
|
||||||
if (onAdd != null)
|
if (onAdd != null)
|
||||||
{
|
{
|
||||||
|
|
@ -651,6 +932,8 @@ namespace Vanguard.UI
|
||||||
var button = row.gameObject.AddComponent<Button>();
|
var button = row.gameObject.AddComponent<Button>();
|
||||||
button.targetGraphic = row.GetComponent<Image>();
|
button.targetGraphic = row.GetComponent<Image>();
|
||||||
button.onClick.AddListener(() => RemoveCard(printingId));
|
button.onClick.AddListener(() => RemoveCard(printingId));
|
||||||
|
if (meta != null)
|
||||||
|
AddRightClick(row.gameObject, printingId, meta);
|
||||||
|
|
||||||
var art = UiKit.CreatePanel(
|
var art = UiKit.CreatePanel(
|
||||||
row, "Art", ClanColor(meta != null ? meta.Clan : "None"));
|
row, "Art", ClanColor(meta != null ? meta.Clan : "None"));
|
||||||
|
|
@ -988,6 +1271,31 @@ namespace Vanguard.UI
|
||||||
_ => "—",
|
_ => "—",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static int DustFor(string rarity) => rarity switch
|
||||||
|
{
|
||||||
|
"C" => 5,
|
||||||
|
"R" => 10,
|
||||||
|
"RR" => 25,
|
||||||
|
"RRR" => 100,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static int CraftCostFor(string rarity) => rarity switch
|
||||||
|
{
|
||||||
|
"C" => 20,
|
||||||
|
"R" => 40,
|
||||||
|
"RR" => 100,
|
||||||
|
"RRR" => 400,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static void SetButtonLabel(Button button, string label)
|
||||||
|
{
|
||||||
|
var text = button.GetComponentInChildren<Text>();
|
||||||
|
if (text != null)
|
||||||
|
text.text = label;
|
||||||
|
}
|
||||||
|
|
||||||
private static Color ClanColor(string clan) => clan switch
|
private static Color ClanColor(string clan) => clan switch
|
||||||
{
|
{
|
||||||
"RoyalPaladin" => new Color(0.22f, 0.42f, 0.78f),
|
"RoyalPaladin" => new Color(0.22f, 0.42f, 0.78f),
|
||||||
|
|
|
||||||
|
|
@ -1,356 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: f00da70463bd90b45bdbf542ddb33bb1
|
|
||||||
|
|
@ -14,7 +14,6 @@ namespace Vanguard.UI
|
||||||
private readonly AuthService _auth;
|
private readonly AuthService _auth;
|
||||||
private readonly PlayerState _player;
|
private readonly PlayerState _player;
|
||||||
private readonly Action _onLoggedOut;
|
private readonly Action _onLoggedOut;
|
||||||
private readonly Action _onCollection;
|
|
||||||
private readonly Action _onDecks;
|
private readonly Action _onDecks;
|
||||||
private readonly Action _onStore;
|
private readonly Action _onStore;
|
||||||
private readonly Action _onHistory;
|
private readonly Action _onHistory;
|
||||||
|
|
@ -41,7 +40,6 @@ namespace Vanguard.UI
|
||||||
AuthService auth,
|
AuthService auth,
|
||||||
PlayerState player,
|
PlayerState player,
|
||||||
Action onLoggedOut,
|
Action onLoggedOut,
|
||||||
Action onCollection,
|
|
||||||
Action onDecks,
|
Action onDecks,
|
||||||
Action onStore,
|
Action onStore,
|
||||||
Action onHistory,
|
Action onHistory,
|
||||||
|
|
@ -51,7 +49,6 @@ namespace Vanguard.UI
|
||||||
_auth = auth;
|
_auth = auth;
|
||||||
_player = player;
|
_player = player;
|
||||||
_onLoggedOut = onLoggedOut;
|
_onLoggedOut = onLoggedOut;
|
||||||
_onCollection = onCollection;
|
|
||||||
_onDecks = onDecks;
|
_onDecks = onDecks;
|
||||||
_onStore = onStore;
|
_onStore = onStore;
|
||||||
_onHistory = onHistory;
|
_onHistory = onHistory;
|
||||||
|
|
@ -112,8 +109,7 @@ namespace Vanguard.UI
|
||||||
UiKit.SetPreferredHeight(actions, 96f);
|
UiKit.SetPreferredHeight(actions, 96f);
|
||||||
UiKit.AddHorizontalLayout(actions, 18, 14);
|
UiKit.AddHorizontalLayout(actions, 18, 14);
|
||||||
|
|
||||||
AddAction(actions, "Коллекция", _onCollection);
|
AddAction(actions, "Коллекция", _onDecks);
|
||||||
AddAction(actions, "Колоды", _onDecks);
|
|
||||||
AddAction(actions, "Магазин", _onStore);
|
AddAction(actions, "Магазин", _onStore);
|
||||||
AddAction(actions, "История", _onHistory);
|
AddAction(actions, "История", _onHistory);
|
||||||
AddAction(actions, "В бой", _onPlay);
|
AddAction(actions, "В бой", _onPlay);
|
||||||
|
|
|
||||||
|
|
@ -1,352 +0,0 @@
|
||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!29 &1
|
|
||||||
OcclusionCullingSettings:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
serializedVersion: 2
|
|
||||||
m_OcclusionBakeSettings:
|
|
||||||
smallestOccluder: 5
|
|
||||||
smallestHole: 0.25
|
|
||||||
backfaceThreshold: 100
|
|
||||||
m_SceneGUID: 00000000000000000000000000000000
|
|
||||||
m_OcclusionCullingData: {fileID: 0}
|
|
||||||
--- !u!104 &2
|
|
||||||
RenderSettings:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
serializedVersion: 10
|
|
||||||
m_Fog: 0
|
|
||||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
|
||||||
m_FogMode: 3
|
|
||||||
m_FogDensity: 0.01
|
|
||||||
m_LinearFogStart: 0
|
|
||||||
m_LinearFogEnd: 300
|
|
||||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
|
||||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
|
||||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
|
||||||
m_AmbientIntensity: 1
|
|
||||||
m_AmbientMode: 0
|
|
||||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
|
||||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
|
||||||
m_HaloStrength: 0.5
|
|
||||||
m_FlareStrength: 1
|
|
||||||
m_FlareFadeSpeed: 3
|
|
||||||
m_HaloTexture: {fileID: 0}
|
|
||||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
|
||||||
m_DefaultReflectionMode: 0
|
|
||||||
m_DefaultReflectionResolution: 128
|
|
||||||
m_ReflectionBounces: 1
|
|
||||||
m_ReflectionIntensity: 1
|
|
||||||
m_CustomReflection: {fileID: 0}
|
|
||||||
m_Sun: {fileID: 0}
|
|
||||||
m_UseRadianceAmbientProbe: 0
|
|
||||||
--- !u!157 &3
|
|
||||||
LightmapSettings:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
serializedVersion: 13
|
|
||||||
m_BakeOnSceneLoad: 0
|
|
||||||
m_GISettings:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_BounceScale: 1
|
|
||||||
m_IndirectOutputScale: 1
|
|
||||||
m_AlbedoBoost: 1
|
|
||||||
m_EnvironmentLightingMode: 0
|
|
||||||
m_EnableBakedLightmaps: 1
|
|
||||||
m_EnableRealtimeLightmaps: 0
|
|
||||||
m_LightmapEditorSettings:
|
|
||||||
serializedVersion: 12
|
|
||||||
m_Resolution: 2
|
|
||||||
m_BakeResolution: 40
|
|
||||||
m_AtlasSize: 1024
|
|
||||||
m_AO: 0
|
|
||||||
m_AOMaxDistance: 1
|
|
||||||
m_CompAOExponent: 1
|
|
||||||
m_CompAOExponentDirect: 0
|
|
||||||
m_ExtractAmbientOcclusion: 0
|
|
||||||
m_Padding: 2
|
|
||||||
m_LightmapParameters: {fileID: 0}
|
|
||||||
m_LightmapsBakeMode: 1
|
|
||||||
m_TextureCompression: 1
|
|
||||||
m_ReflectionCompression: 2
|
|
||||||
m_MixedBakeMode: 2
|
|
||||||
m_BakeBackend: 2
|
|
||||||
m_PVRSampling: 1
|
|
||||||
m_PVRDirectSampleCount: 32
|
|
||||||
m_PVRSampleCount: 512
|
|
||||||
m_PVRBounces: 2
|
|
||||||
m_PVREnvironmentSampleCount: 256
|
|
||||||
m_PVREnvironmentReferencePointCount: 2048
|
|
||||||
m_PVRFilteringMode: 1
|
|
||||||
m_PVRDenoiserTypeDirect: 2
|
|
||||||
m_PVRDenoiserTypeIndirect: 2
|
|
||||||
m_PVRDenoiserTypeAO: 2
|
|
||||||
m_PVRFilterTypeDirect: 0
|
|
||||||
m_PVRFilterTypeIndirect: 0
|
|
||||||
m_PVRFilterTypeAO: 0
|
|
||||||
m_PVREnvironmentMIS: 1
|
|
||||||
m_PVRCulling: 1
|
|
||||||
m_PVRFilteringGaussRadiusDirect: 1
|
|
||||||
m_PVRFilteringGaussRadiusIndirect: 1
|
|
||||||
m_PVRFilteringGaussRadiusAO: 1
|
|
||||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
|
||||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
|
||||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
|
||||||
m_ExportTrainingData: 0
|
|
||||||
m_TrainingDataDestination: TrainingData
|
|
||||||
m_LightProbeSampleCountMultiplier: 4
|
|
||||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
|
||||||
m_LightingSettings: {fileID: 0}
|
|
||||||
--- !u!196 &4
|
|
||||||
NavMeshSettings:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_BuildSettings:
|
|
||||||
serializedVersion: 3
|
|
||||||
agentTypeID: 0
|
|
||||||
agentRadius: 0.5
|
|
||||||
agentHeight: 2
|
|
||||||
agentSlope: 45
|
|
||||||
agentClimb: 0.4
|
|
||||||
ledgeDropHeight: 0
|
|
||||||
maxJumpAcrossDistance: 0
|
|
||||||
minRegionArea: 2
|
|
||||||
manualCellSize: 0
|
|
||||||
cellSize: 0.16666667
|
|
||||||
manualTileSize: 0
|
|
||||||
tileSize: 256
|
|
||||||
buildHeightMesh: 0
|
|
||||||
maxJobWorkers: 0
|
|
||||||
preserveTilesOutsideBounds: 0
|
|
||||||
debug:
|
|
||||||
m_Flags: 0
|
|
||||||
m_NavMeshData: {fileID: 0}
|
|
||||||
--- !u!1 &490282527
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 490282529}
|
|
||||||
- component: {fileID: 490282528}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: Collection Scene
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!114 &490282528
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 490282527}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 8ccb09cec5c89814493fae05cf2d6051, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Vanguard.Runtime::Vanguard.App.CollectionSceneController
|
|
||||||
--- !u!4 &490282529
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 490282527}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
|
||||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 0}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!1 &1697705437
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 1697705439}
|
|
||||||
- component: {fileID: 1697705438}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: Directional Light
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!108 &1697705438
|
|
||||||
Light:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1697705437}
|
|
||||||
m_Enabled: 1
|
|
||||||
serializedVersion: 13
|
|
||||||
m_Type: 1
|
|
||||||
m_Color: {r: 0.82, g: 0.88, b: 1, a: 1}
|
|
||||||
m_Intensity: 0.9
|
|
||||||
m_Range: 10
|
|
||||||
m_SpotAngle: 30
|
|
||||||
m_InnerSpotAngle: 21.80208
|
|
||||||
m_CookieSize2D: {x: 0.5, y: 0.5}
|
|
||||||
m_Shadows:
|
|
||||||
m_Type: 2
|
|
||||||
m_Resolution: -1
|
|
||||||
m_CustomResolution: -1
|
|
||||||
m_Strength: 1
|
|
||||||
m_Bias: 0.05
|
|
||||||
m_NormalBias: 0.4
|
|
||||||
m_NearPlane: 0.2
|
|
||||||
m_CullingMatrixOverride:
|
|
||||||
e00: 1
|
|
||||||
e01: 0
|
|
||||||
e02: 0
|
|
||||||
e03: 0
|
|
||||||
e10: 0
|
|
||||||
e11: 1
|
|
||||||
e12: 0
|
|
||||||
e13: 0
|
|
||||||
e20: 0
|
|
||||||
e21: 0
|
|
||||||
e22: 1
|
|
||||||
e23: 0
|
|
||||||
e30: 0
|
|
||||||
e31: 0
|
|
||||||
e32: 0
|
|
||||||
e33: 1
|
|
||||||
m_UseCullingMatrixOverride: 0
|
|
||||||
m_Cookie: {fileID: 0}
|
|
||||||
m_DrawHalo: 0
|
|
||||||
m_Flare: {fileID: 0}
|
|
||||||
m_RenderMode: 0
|
|
||||||
m_CullingMask:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_RenderingLayerMask: 1
|
|
||||||
m_Lightmapping: 4
|
|
||||||
m_LightShadowCasterMode: 0
|
|
||||||
m_AreaSize: {x: 1, y: 1}
|
|
||||||
m_BounceIntensity: 1
|
|
||||||
m_ColorTemperature: 6570
|
|
||||||
m_UseColorTemperature: 0
|
|
||||||
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
|
|
||||||
m_UseBoundingSphereOverride: 0
|
|
||||||
m_UseViewFrustumForShadowCasterCull: 1
|
|
||||||
m_ForceVisible: 0
|
|
||||||
m_ShapeRadius: 0.025
|
|
||||||
m_ShadowAngle: 0
|
|
||||||
m_LightUnit: 1
|
|
||||||
m_LuxAtDistance: 1
|
|
||||||
m_EnableSpotReflector: 1
|
|
||||||
--- !u!4 &1697705439
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1697705437}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: 0.39098036, y: -0.25180724, z: 0.112111814, w: 0.87815624}
|
|
||||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 0}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!1 &1894381784
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 1894381786}
|
|
||||||
- component: {fileID: 1894381785}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: Main Camera
|
|
||||||
m_TagString: MainCamera
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!20 &1894381785
|
|
||||||
Camera:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1894381784}
|
|
||||||
m_Enabled: 1
|
|
||||||
serializedVersion: 2
|
|
||||||
m_ClearFlags: 2
|
|
||||||
m_BackGroundColor: {r: 0.02, g: 0.03, b: 0.055, a: 1}
|
|
||||||
m_projectionMatrixMode: 1
|
|
||||||
m_GateFitMode: 2
|
|
||||||
m_FOVAxisMode: 0
|
|
||||||
m_Iso: 200
|
|
||||||
m_ShutterSpeed: 0.005
|
|
||||||
m_Aperture: 16
|
|
||||||
m_FocusDistance: 10
|
|
||||||
m_FocalLength: 50
|
|
||||||
m_BladeCount: 5
|
|
||||||
m_Curvature: {x: 2, y: 11}
|
|
||||||
m_BarrelClipping: 0.25
|
|
||||||
m_Anamorphism: 0
|
|
||||||
m_SensorSize: {x: 36, y: 24}
|
|
||||||
m_LensShift: {x: 0, y: 0}
|
|
||||||
m_NormalizedViewPortRect:
|
|
||||||
serializedVersion: 2
|
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
width: 1
|
|
||||||
height: 1
|
|
||||||
near clip plane: 0.3
|
|
||||||
far clip plane: 1000
|
|
||||||
field of view: 60
|
|
||||||
orthographic: 0
|
|
||||||
orthographic size: 5
|
|
||||||
m_Depth: 0
|
|
||||||
m_CullingMask:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_RenderingPath: -1
|
|
||||||
m_TargetTexture: {fileID: 0}
|
|
||||||
m_TargetDisplay: 0
|
|
||||||
m_TargetEye: 3
|
|
||||||
m_HDR: 1
|
|
||||||
m_AllowMSAA: 1
|
|
||||||
m_AllowDynamicResolution: 0
|
|
||||||
m_ForceIntoRT: 0
|
|
||||||
m_OcclusionCulling: 1
|
|
||||||
m_StereoConvergence: 10
|
|
||||||
m_StereoSeparation: 0.022
|
|
||||||
--- !u!4 &1894381786
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1894381784}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: 0.104528464, y: 0, z: 0, w: 0.9945219}
|
|
||||||
m_LocalPosition: {x: 0, y: 3.2, z: -10}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 0}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!1660057539 &9223372036854775807
|
|
||||||
SceneRoots:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_Roots:
|
|
||||||
- {fileID: 490282529}
|
|
||||||
- {fileID: 1894381786}
|
|
||||||
- {fileID: 1697705439}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: cd8d654fe14bfb0419574bc6d2820591
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -33,11 +33,9 @@ namespace Vanguard.Tests
|
||||||
|
|
||||||
var lobby = new MetaView(
|
var lobby = new MetaView(
|
||||||
canvas.transform, auth, player, () => { },
|
canvas.transform, auth, player, () => { },
|
||||||
() => { }, () => { }, () => { }, () => { }, () => { }, () => { });
|
() => { }, () => { }, () => { }, () => { }, () => { });
|
||||||
var onboarding = new OnboardingView(
|
var onboarding = new OnboardingView(
|
||||||
canvas.transform, auth, meta, () => { }, () => { });
|
canvas.transform, auth, meta, () => { }, () => { });
|
||||||
var collection = new CollectionView(
|
|
||||||
canvas.transform, meta, player, () => { });
|
|
||||||
var decks = new DeckBuilderView(
|
var decks = new DeckBuilderView(
|
||||||
canvas.transform, meta, auth, () => { }, _ => { });
|
canvas.transform, meta, auth, () => { }, _ => { });
|
||||||
var queue = new QueueView(
|
var queue = new QueueView(
|
||||||
|
|
@ -52,7 +50,6 @@ namespace Vanguard.Tests
|
||||||
Assert.IsNotNull(GameObject.Find("LoginScreen"));
|
Assert.IsNotNull(GameObject.Find("LoginScreen"));
|
||||||
Assert.IsTrue(Exists("MetaScreen"));
|
Assert.IsTrue(Exists("MetaScreen"));
|
||||||
Assert.IsTrue(Exists("OnboardingScreen"));
|
Assert.IsTrue(Exists("OnboardingScreen"));
|
||||||
Assert.IsTrue(Exists("CollectionScreen"));
|
|
||||||
Assert.IsTrue(Exists("DeckBuilderScreen"));
|
Assert.IsTrue(Exists("DeckBuilderScreen"));
|
||||||
Assert.IsTrue(Exists("StoreScreen"));
|
Assert.IsTrue(Exists("StoreScreen"));
|
||||||
Assert.IsTrue(Exists("HistoryScreen"));
|
Assert.IsTrue(Exists("HistoryScreen"));
|
||||||
|
|
@ -62,7 +59,6 @@ namespace Vanguard.Tests
|
||||||
|
|
||||||
// После смены сцены старые views не должны реагировать на PlayerState.
|
// После смены сцены старые views не должны реагировать на PlayerState.
|
||||||
lobby.Dispose();
|
lobby.Dispose();
|
||||||
collection.Dispose();
|
|
||||||
store.Dispose();
|
store.Dispose();
|
||||||
player.Apply(new MeResponse
|
player.Apply(new MeResponse
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@ EditorBuildSettings:
|
||||||
- enabled: 1
|
- enabled: 1
|
||||||
path: Assets/Vanguard/Scenes/Lobby.unity
|
path: Assets/Vanguard/Scenes/Lobby.unity
|
||||||
guid: 59a25f3bae0ec7c489d2a139d7eea285
|
guid: 59a25f3bae0ec7c489d2a139d7eea285
|
||||||
- enabled: 1
|
|
||||||
path: Assets/Vanguard/Scenes/Collection.unity
|
|
||||||
guid: cd8d654fe14bfb0419574bc6d2820591
|
|
||||||
- enabled: 1
|
- enabled: 1
|
||||||
path: Assets/Vanguard/Scenes/DeckBuilder.unity
|
path: Assets/Vanguard/Scenes/DeckBuilder.unity
|
||||||
guid: b8567d3f2f60dcc4f85794bdbaf95b45
|
guid: b8567d3f2f60dcc4f85794bdbaf95b45
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ http://localhost:5080
|
||||||
## Визуальное направление
|
## Визуальное направление
|
||||||
|
|
||||||
Клиент делаем в **3D** на URP. Основные экраны — отдельные сцены:
|
Клиент делаем в **3D** на URP. Основные экраны — отдельные сцены:
|
||||||
**Login**, **Lobby**, **Collection**, **DeckBuilder**, **Battle**.
|
**Login**, **Lobby**, **DeckBuilder**, **Battle**.
|
||||||
`Boot` — служебная сцена с общими сервисами; очередь и онбординг —
|
`DeckBuilder` совмещает коллекцию и редактор колоды. `Boot` — служебная
|
||||||
оверлеи Lobby. Текущий единый Canvas — временный gray-box, его разносим
|
сцена с общими сервисами; очередь и онбординг — оверлеи Lobby. Текущий единый Canvas — временный gray-box, его разносим
|
||||||
по сценам. Полное решение — `docs/client-visual-direction.md`.
|
по сценам. Полное решение — `docs/client-visual-direction.md`.
|
||||||
|
|
||||||
## Что уже есть
|
## Что уже есть
|
||||||
|
|
@ -49,10 +49,10 @@ http://localhost:5080
|
||||||
- Логин, регистрация, refresh, `/me`, выход.
|
- Логин, регистрация, refresh, `/me`, выход.
|
||||||
- Восстановление сессии по refresh-токену при старте.
|
- Восстановление сессии по refresh-токену при старте.
|
||||||
- Онбординг: две триал-деки из `/api/store`, выбор одной, 50 карт и 5 токенов.
|
- Онбординг: две триал-деки из `/api/store`, выбор одной, 50 карт и 5 токенов.
|
||||||
- Коллекция: список карт, балансы, разбор в пыль с подтверждением.
|
- Коллекция и колоды: карточная сетка, сортировка клан→ранг→имя,
|
||||||
- Колоды: список справа, создание/редактирование в декбилдере;
|
фильтры, статистика по рангам, добавление/удаление карт кликом.
|
||||||
сортировка коллекции клан→ранг→имя, фильтры, статистика по рангам,
|
- ПКМ по карте — оверлей с картой, способностями, разбором в пыль и
|
||||||
добавление/удаление карт кликом.
|
крафтом за пыль.
|
||||||
- Магазин в Lobby: бустер за Gold/токен, показ 5 карт, покупка второй
|
- Магазин в Lobby: бустер за Gold/токен, показ 5 карт, покупка второй
|
||||||
триал-деки.
|
триал-деки.
|
||||||
- История матчей: список результатов, награды, MMR; просмотр реплея по
|
- История матчей: список результатов, награды, MMR; просмотр реплея по
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ MVP: бустер **BT01** + триал-деки **TD01/TD02** — 94 карты
|
||||||
| Протокол и контракты | `docs/protocol.md`, `Vanguard.Shared` готовы |
|
| Протокол и контракты | `docs/protocol.md`, `Vanguard.Shared` готовы |
|
||||||
| Сервер | сеть, матчмейкинг, PostgreSQL + Redis, Orleans, Docker |
|
| Сервер | сеть, матчмейкинг, PostgreSQL + Redis, Orleans, Docker |
|
||||||
| Мета-API | JWT, коллекция, колоды, магазин, экономика, история, реплеи |
|
| Мета-API | JWT, коллекция, колоды, магазин, экономика, история, реплеи |
|
||||||
| Клиент | Unity 6.6, 3D-сцены Login/Lobby/Collection/DeckBuilder/Battle, логин/коллекция/колоды/очередь/gray-box бой |
|
| Клиент | Unity 6.6, 3D-сцены Login/Lobby/DeckBuilder/Battle, логин/коллекция/колоды/очередь/gray-box бой |
|
||||||
| Лаунчер | не начат |
|
| Лаунчер | не начат |
|
||||||
|
|
||||||
Блок «Игровое ядро» из `IMPLEMENTATION_CHECKLIST.md` закрыт по его критериям:
|
Блок «Игровое ядро» из `IMPLEMENTATION_CHECKLIST.md` закрыт по его критериям:
|
||||||
|
|
@ -526,8 +526,8 @@ CI подключён и проходит; серверный блок закр
|
||||||
- проект создан из URP-шаблона Unity 6.6; пакеты: URP, Input System,
|
- проект создан из URP-шаблона Unity 6.6; пакеты: URP, Input System,
|
||||||
Addressables, Localization, Newtonsoft.Json, VContainer (OpenUPM);
|
Addressables, Localization, Newtonsoft.Json, VContainer (OpenUPM);
|
||||||
- визуальное направление зафиксировано: **3D**, отдельные сцены-экраны
|
- визуальное направление зафиксировано: **3D**, отдельные сцены-экраны
|
||||||
Login/Lobby/Collection/DeckBuilder/Battle (`docs/client-visual-direction.md`);
|
Login/Lobby/DeckBuilder/Battle (`docs/client-visual-direction.md`);
|
||||||
сцены и `SceneFlow` уже созданы, UI пока code-generated gray-box;
|
`DeckBuilder` совмещает коллекцию и колоды; UI пока code-generated gray-box;
|
||||||
- сцена `Boot` + runtime-инициализатор, поэтому клиент стартует и в пустой
|
- сцена `Boot` + runtime-инициализатор, поэтому клиент стартует и в пустой
|
||||||
сцене;
|
сцене;
|
||||||
- composition root на VContainer: `AppConfig`, `TokenStore`, `ApiClient`,
|
- composition root на VContainer: `AppConfig`, `TokenStore`, `ApiClient`,
|
||||||
|
|
@ -541,9 +541,10 @@ CI подключён и проходит; серверный блок закр
|
||||||
переходами в разделы;
|
переходами в разделы;
|
||||||
- онбординг: список триал-дек из `/api/store`, выбор одной, 50 карт + 5
|
- онбординг: список триал-дек из `/api/store`, выбор одной, 50 карт + 5
|
||||||
токенов, обновление профиля;
|
токенов, обновление профиля;
|
||||||
- коллекция: `/api/collection`, балансы, разбор в пыль с подтверждением;
|
- коллекция: `/api/collection`, балансы, разбор в пыль и крафт; ПКМ по
|
||||||
- колоды: `/api/decks`, удаление и автосборка стартовой колоды из
|
карте открывает оверлей с текстом способностей;
|
||||||
выбранной триал-деки (50 карт);
|
- колоды: `/api/decks`, список справа, редактор; карточная сетка
|
||||||
|
коллекции с сортировкой клан→ранг→имя;
|
||||||
- магазин в Lobby: `/api/store`, бустер за Gold/токен с показом 5 карт,
|
- магазин в Lobby: `/api/store`, бустер за Gold/токен с показом 5 карт,
|
||||||
покупка второй триал-деки;
|
покупка второй триал-деки;
|
||||||
- история матчей и реплей: `/api/matches`, `/api/replays/{id}`, просмотр
|
- история матчей и реплей: `/api/matches`, `/api/replays/{id}`, просмотр
|
||||||
|
|
@ -555,7 +556,7 @@ CI подключён и проходит; серверный блок закр
|
||||||
сдача с подтверждением, результат;
|
сдача с подтверждением, результат;
|
||||||
- `Vanguard.Shared.dll` (netstandard2.1) и MessagePack 3.1.12 подключены
|
- `Vanguard.Shared.dll` (netstandard2.1) и MessagePack 3.1.12 подключены
|
||||||
как плагины; `client/README.md` описывает запуск и ограничения;
|
как плагины; `client/README.md` описывает запуск и ограничения;
|
||||||
- сцены `Login`, `Lobby`, `Collection`, `DeckBuilder`, `Battle` и
|
- сцены `Login`, `Lobby`, `DeckBuilder`, `Battle` и
|
||||||
`SceneFlow`; `Boot` — стартовая служебная сцена, `VanguardApp` и сервисы
|
`SceneFlow`; `Boot` — стартовая служебная сцена, `VanguardApp` и сервисы
|
||||||
живут между сценами;
|
живут между сценами;
|
||||||
- 3D-фон на каждой сцене: камера, свет, пол и тематические примитивы;
|
- 3D-фон на каждой сцене: камера, свет, пол и тематические примитивы;
|
||||||
|
|
@ -574,13 +575,13 @@ CI подключён и проходит; серверный блок закр
|
||||||
4. Локализация RU/EN, настройки, DOTween.
|
4. Локализация RU/EN, настройки, DOTween.
|
||||||
|
|
||||||
**Зафиксированное визуальное направление** — `docs/client-visual-direction.md`:
|
**Зафиксированное визуальное направление** — `docs/client-visual-direction.md`:
|
||||||
3D-клиент на URP, отдельные сцены-экраны Login / Lobby / Collection /
|
3D-клиент на URP, отдельные сцены-экраны Login / Lobby / DeckBuilder /
|
||||||
DeckBuilder / Battle; Boot и общие сервисы остаются персистентными.
|
Battle; Boot и общие сервисы остаются персистентными.
|
||||||
Сцены и `SceneFlow` уже созданы; UI пока code-generated gray-box.
|
Сцены и `SceneFlow` уже созданы; UI пока code-generated gray-box.
|
||||||
|
|
||||||
- Unity 6 LTS, URP, Addressables, Input System, Localization, DOTween.
|
- Unity 6 LTS, URP, Addressables, Input System, Localization, DOTween.
|
||||||
- VContainer, `ApiClient`, `GameConnection`, `SceneFlow`.
|
- VContainer, `ApiClient`, `GameConnection`, `SceneFlow`.
|
||||||
- Сцены: `Boot` (служебная), `Login`, `Lobby`, `Collection`, `DeckBuilder`,
|
- Сцены: `Boot` (служебная), `Login`, `Lobby`, `DeckBuilder`,
|
||||||
`Battle`; очередь/онбординг — оверлеи Lobby.
|
`Battle`; очередь/онбординг — оверлеи Lobby.
|
||||||
- История матчей и экран реплея: `GET /api/matches`, `GET /api/replays/{id}`,
|
- История матчей и экран реплея: `GET /api/matches`, `GET /api/replays/{id}`,
|
||||||
проигрывание `frames` + `events`.
|
проигрывание `frames` + `events`.
|
||||||
|
|
@ -720,10 +721,15 @@ DeckBuilder / Battle; Boot и общие сервисы остаются пер
|
||||||
- сортировка коллекции: клан → ранг → имя.
|
- сортировка коллекции: клан → ранг → имя.
|
||||||
- Ограничения в UI: 50 карт, ≤4 по `nameId`, не больше owned-копий;
|
- Ограничения в UI: 50 карт, ≤4 по `nameId`, не больше owned-копий;
|
||||||
серверная валидация остаётся источником истины.
|
серверная валидация остаётся источником истины.
|
||||||
- `DeckBuilderSceneController` и `CollectionSceneController` теперь
|
- Сцена **Collection** и отдельный `CollectionView` удалены; коллекция
|
||||||
вызывают `Show()`; раньше сцены создавали скрытый корень.
|
теперь на экране **Коллекция** внутри `DeckBuilderView`.
|
||||||
- `MetaService.GetCatalogAsync`, `UpdateDeckAsync`; `ApiClient.PutAsync`.
|
- ПКМ по карте коллекции/колоды открывает оверлей: изображение-заглушка,
|
||||||
|
статы, текст способности, разбор в пыль и крафт (C/R/RR/RRR; SP и TD
|
||||||
|
нельзя).
|
||||||
|
- `MetaService.CraftAsync`; `GET /api/cards` отдаёт `power`, `shield`,
|
||||||
|
`critical`, `type`, `textEn/textRu` и флаги способностей.
|
||||||
- Проверки: серверные 131 + 44 на `ssh home`; Unity EditMode 2/2,
|
- Проверки: серверные 131 + 44 на `ssh home`; Unity EditMode 2/2,
|
||||||
`SCENE_SMOKE success=True active=Login`; live `GET /api/cards` — 124
|
`SCENE_SMOKE success=True active=Login`; live `GET /api/cards` — 124
|
||||||
принта; live UI-проба: загрузка 50 карт, создание колоды,
|
принта; live UI-проба: загрузка 50 карт, создание колоды,
|
||||||
добавление и удаление карты.
|
добавление/удаление, ПКМ-оверлей, текст способности и разбор карты;
|
||||||
|
live API-проба крафта: разбор 4 C → 20 пыли → крафт C.
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,9 @@
|
||||||
- Пакеты: URP, Input System, Addressables, Localization, Newtonsoft.Json,
|
- Пакеты: URP, Input System, Addressables, Localization, Newtonsoft.Json,
|
||||||
VContainer (OpenUPM).
|
VContainer (OpenUPM).
|
||||||
- Визуальное направление: **3D**, отдельные сцены-экраны:
|
- Визуальное направление: **3D**, отдельные сцены-экраны:
|
||||||
`Login`, `Lobby`, `Collection`, `DeckBuilder`, `Battle`.
|
`Login`, `Lobby`, `DeckBuilder`, `Battle`.
|
||||||
`Boot` — стартовая служебная сцена; `VanguardApp` персистентный.
|
`DeckBuilder` совмещает коллекцию и колоды; `Boot` — стартовая служебная
|
||||||
|
сцена; `VanguardApp` персистентный.
|
||||||
- Переходы — `SceneFlow`; очередь/онбординг — оверлеи **Lobby**.
|
- Переходы — `SceneFlow`; очередь/онбординг — оверлеи **Lobby**.
|
||||||
- 3D-окружение сейчас из примитивов (`SceneEnvironment`): камера, свет,
|
- 3D-окружение сейчас из примитивов (`SceneEnvironment`): камера, свет,
|
||||||
пол, тематические кубы. UI — code-generated uGUI, пока gray-box.
|
пол, тематические кубы. UI — code-generated uGUI, пока gray-box.
|
||||||
|
|
@ -46,8 +47,13 @@
|
||||||
WebSocket-матч (snapshot, интенты, маллиган, сдача, `GameFinished`),
|
WebSocket-матч (snapshot, интенты, маллиган, сдача, `GameFinished`),
|
||||||
таймер, результат.
|
таймер, результат.
|
||||||
- Декбилдер: `DeckBuilderView`; каталог метаданных — `GET /api/cards`
|
- Декбилдер: `DeckBuilderView`; каталог метаданных — `GET /api/cards`
|
||||||
(grade, trigger, clan, rarity, printing), клиент объединяет с коллекцией.
|
(grade, trigger, clan, rarity, printing, power/shield/type, текст
|
||||||
|
способности), клиент объединяет с коллекцией.
|
||||||
Изображения карт пока заглушки-плашки по цвету клана.
|
Изображения карт пока заглушки-плашки по цвету клана.
|
||||||
|
- ПКМ по карте — оверлей с картой, способностями, разбором в пыль и
|
||||||
|
крафтом; SP/TD не разбираются и не крафтятся.
|
||||||
|
- Отдельная сцена `Collection` и `CollectionView` удалены; коллекция
|
||||||
|
открывается из Lobby кнопкой «Коллекция» (сцена `DeckBuilder`).
|
||||||
- Не готово: арты/модели/анимации, локализация RU/EN, настройки, DOTween.
|
- Не готово: арты/модели/анимации, локализация RU/EN, настройки, DOTween.
|
||||||
|
|
||||||
## Запуск
|
## Запуск
|
||||||
|
|
@ -93,17 +99,16 @@ curl http://127.0.0.1:18080/health
|
||||||
```
|
```
|
||||||
client/Assets/Vanguard/Runtime/App/
|
client/Assets/Vanguard/Runtime/App/
|
||||||
VanguardApp.cs composition root, персистентный
|
VanguardApp.cs composition root, персистентный
|
||||||
SceneFlow.cs переходы Login/Lobby/Collection/DeckBuilder/Battle
|
SceneFlow.cs переходы Login/Lobby/DeckBuilder/Battle
|
||||||
SceneEnvironment.cs 3D-фон сцен
|
SceneEnvironment.cs 3D-фон сцен
|
||||||
SceneControllerBase.cs
|
SceneControllerBase.cs
|
||||||
LoginSceneController.cs
|
LoginSceneController.cs
|
||||||
LobbySceneController.cs
|
LobbySceneController.cs
|
||||||
CollectionSceneController.cs
|
|
||||||
DeckBuilderSceneController.cs
|
DeckBuilderSceneController.cs
|
||||||
BattleSceneController.cs
|
BattleSceneController.cs
|
||||||
client/Assets/Vanguard/Runtime/UI/
|
client/Assets/Vanguard/Runtime/UI/
|
||||||
LoginView.cs MetaView.cs OnboardingView.cs
|
LoginView.cs MetaView.cs OnboardingView.cs
|
||||||
CollectionView.cs DeckBuilderView.cs QueueView.cs BattleView.cs
|
DeckBuilderView.cs QueueView.cs BattleView.cs
|
||||||
UiKit.cs
|
UiKit.cs
|
||||||
client/Assets/Vanguard/Runtime/Battle/GameConnection.cs
|
client/Assets/Vanguard/Runtime/Battle/GameConnection.cs
|
||||||
client/Assets/Vanguard/Runtime/Api/ApiClient.cs + ApiContracts.cs
|
client/Assets/Vanguard/Runtime/Api/ApiClient.cs + ApiContracts.cs
|
||||||
|
|
@ -145,4 +150,4 @@ client/Assets/Plugins/Vanguard.Shared + MessagePack
|
||||||
- `UiKit.CreateCanvas` по умолчанию **не** персистентный: Canvas принадлежит
|
- `UiKit.CreateCanvas` по умолчанию **не** персистентный: Canvas принадлежит
|
||||||
сцене; EventSystem остаётся персистентным.
|
сцене; EventSystem остаётся персистентным.
|
||||||
- Сцена `Boot` должна быть первой в Build Settings; сейчас там
|
- Сцена `Boot` должна быть первой в Build Settings; сейчас там
|
||||||
`Boot`, `Login`, `Lobby`, `Collection`, `DeckBuilder`, `Battle`.
|
`Boot`, `Login`, `Lobby`, `DeckBuilder`, `Battle`.
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,8 @@
|
||||||
камерой, светом и UI-слоем:
|
камерой, светом и UI-слоем:
|
||||||
1. **Login** — вход/регистрация;
|
1. **Login** — вход/регистрация;
|
||||||
2. **Lobby** — лобби, профиль, выбор режима, очередь, магазин/история;
|
2. **Lobby** — лобби, профиль, выбор режима, очередь, магазин/история;
|
||||||
3. **Collection** — коллекция карт;
|
3. **DeckBuilder** — коллекция и конструктор колоды в одной сцене;
|
||||||
4. **DeckBuilder** — конструктор колоды;
|
4. **Battle** — боевой стол.
|
||||||
5. **Battle** — боевой стол.
|
|
||||||
- **Boot** — служебная сцена: поднимает `VanguardApp`, DI-контейнер,
|
- **Boot** — служебная сцена: поднимает `VanguardApp`, DI-контейнер,
|
||||||
сервисы, сеть, аудио и локализацию. Пользователю не показывается.
|
сервисы, сеть, аудио и локализацию. Пользователю не показывается.
|
||||||
- Очередь, онбординг и настройки — **оверлеи внутри Lobby**, отдельными
|
- Очередь, онбординг и настройки — **оверлеи внутри Lobby**, отдельными
|
||||||
|
|
@ -35,7 +34,7 @@
|
||||||
|
|
||||||
## Статус миграции
|
## Статус миграции
|
||||||
|
|
||||||
Сцены `Login`, `Lobby`, `Collection`, `DeckBuilder`, `Battle` созданы;
|
Сцены `Login`, `Lobby`, `DeckBuilder`, `Battle` созданы;
|
||||||
`VanguardApp` и общие сервисы персистентны, `SceneFlow` загружает сцены.
|
`VanguardApp` и общие сервисы персистентны, `SceneFlow` загружает сцены.
|
||||||
Сейчас 3D-окружение собрано из примитивов, UI — code-generated uGUI.
|
Сейчас 3D-окружение собрано из примитивов, UI — code-generated uGUI.
|
||||||
Дальше: арт/модели, prefab-экраны, анимации и наполнение сцен.
|
Дальше: арт/модели, prefab-экраны, анимации и наполнение сцен.
|
||||||
|
|
@ -44,8 +43,7 @@
|
||||||
|
|
||||||
- `LoginView` → сцена **Login** (3D-фон/стол, форма поверх);
|
- `LoginView` → сцена **Login** (3D-фон/стол, форма поверх);
|
||||||
- `MetaView`/`OnboardingView`/`QueueView` → сцена **Lobby**;
|
- `MetaView`/`OnboardingView`/`QueueView` → сцена **Lobby**;
|
||||||
- `CollectionView` → сцена **Collection**;
|
- `DeckBuilderView` → сцена **DeckBuilder** (коллекция + редактор колоды);
|
||||||
- `DecksView` → сцена **DeckBuilder**;
|
|
||||||
- `BattleView` → сцена **Battle**.
|
- `BattleView` → сцена **Battle**.
|
||||||
|
|
||||||
Сетевые и доменные классы (`GameConnection`, `ApiClient`, `MetaService`)
|
Сетевые и доменные классы (`GameConnection`, `ApiClient`, `MetaService`)
|
||||||
|
|
|
||||||
|
|
@ -188,19 +188,30 @@ GET /api/cards [Bearer]
|
||||||
"printingId": "BT01/034EN",
|
"printingId": "BT01/034EN",
|
||||||
"nameId": "BT01-034",
|
"nameId": "BT01-034",
|
||||||
"nameEn": "Dragon Monk, Ginkai",
|
"nameEn": "Dragon Monk, Ginkai",
|
||||||
|
"nameRu": null,
|
||||||
"rarity": "C",
|
"rarity": "C",
|
||||||
"grade": 2,
|
"grade": 2,
|
||||||
|
"type": "NormalUnit",
|
||||||
|
"power": 7000,
|
||||||
|
"shield": 5000,
|
||||||
|
"critical": 1,
|
||||||
"trigger": "None",
|
"trigger": "None",
|
||||||
"clan": "Tachikaze",
|
"clan": "Tachikaze",
|
||||||
"isSentinel": false,
|
"isSentinel": false,
|
||||||
|
"isRestraint": false,
|
||||||
|
"hasBoost": false,
|
||||||
|
"hasIntercept": true,
|
||||||
"isParallel": false,
|
"isParallel": false,
|
||||||
"basePrintingId": null
|
"basePrintingId": null,
|
||||||
|
"textEn": "[AUTO](VC/RC):…",
|
||||||
|
"textRu": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
`grade` — 0–3, `trigger` — `None/Critical/Draw/Stand/Heal`,
|
`grade` — 0–3, `trigger` — `None/Critical/Draw/Stand/Heal`,
|
||||||
`clan` — имя клана, `rarity` — `C/R/RR/RRR/SP/TD`.
|
`clan` — имя клана, `rarity` — `C/R/RR/RRR/SP/TD`. `textEn/textRu` —
|
||||||
|
текст способностей для оверлея карты (RU может быть null).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,23 @@ public sealed record CardCatalogItemDto(
|
||||||
string PrintingId,
|
string PrintingId,
|
||||||
string NameId,
|
string NameId,
|
||||||
string NameEn,
|
string NameEn,
|
||||||
|
string? NameRu,
|
||||||
string Rarity,
|
string Rarity,
|
||||||
int Grade,
|
int Grade,
|
||||||
|
string Type,
|
||||||
|
int Power,
|
||||||
|
int Shield,
|
||||||
|
int Critical,
|
||||||
string Trigger,
|
string Trigger,
|
||||||
string Clan,
|
string Clan,
|
||||||
bool IsSentinel,
|
bool IsSentinel,
|
||||||
|
bool IsRestraint,
|
||||||
|
bool HasBoost,
|
||||||
|
bool HasIntercept,
|
||||||
bool IsParallel,
|
bool IsParallel,
|
||||||
string? BasePrintingId);
|
string? BasePrintingId,
|
||||||
|
string? TextEn,
|
||||||
|
string? TextRu);
|
||||||
|
|
||||||
public static class MetaEndpoints
|
public static class MetaEndpoints
|
||||||
{
|
{
|
||||||
|
|
@ -86,13 +96,23 @@ public static class MetaEndpoints
|
||||||
printing.PrintingId,
|
printing.PrintingId,
|
||||||
card.NameId,
|
card.NameId,
|
||||||
card.NameEn,
|
card.NameEn,
|
||||||
|
card.NameRu,
|
||||||
printing.Rarity.ToString(),
|
printing.Rarity.ToString(),
|
||||||
card.Grade,
|
card.Grade,
|
||||||
|
card.Type.ToString(),
|
||||||
|
card.Power,
|
||||||
|
card.Shield,
|
||||||
|
card.Critical,
|
||||||
card.Trigger.ToString(),
|
card.Trigger.ToString(),
|
||||||
card.Clan.ToString(),
|
card.Clan.ToString(),
|
||||||
card.IsSentinel,
|
card.IsSentinel,
|
||||||
|
card.IsRestraint,
|
||||||
|
card.HasBoost,
|
||||||
|
card.HasIntercept,
|
||||||
printing.IsParallel,
|
printing.IsParallel,
|
||||||
printing.BasePrintingId);
|
printing.BasePrintingId,
|
||||||
|
card.TextEn,
|
||||||
|
card.TextRu);
|
||||||
})
|
})
|
||||||
.OrderBy(item => item.NameEn, StringComparer.Ordinal)
|
.OrderBy(item => item.NameEn, StringComparer.Ordinal)
|
||||||
.ThenBy(item => item.PrintingId, StringComparer.Ordinal)
|
.ThenBy(item => item.PrintingId, StringComparer.Ordinal)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue