client: DeckBuilder — фильтры, карты вне коллекции и «Распылить всё»
CI / test (push) Successful in 5m53s
Details
CI / test (push) Successful in 5m53s
Details
- Кнопка «Сброс» убрана из строки фильтров; сброс остался в оверлее. - В фильтрах добавлен режим «Показывать вне коллекции»: карты без копий показываются серыми, без добавления в колоду (ПКМ — карточка). - Рядом с «Фильтры» — «Распылить всё», если есть копии сверх 4 на название (SP пропускаются). Подтверждение показывает количество и пыль. - DeckListModel.PlanExtras + тест; EditMode 70/70, standalone собран.
This commit is contained in:
parent
c7f2c51fec
commit
42e57f5ac1
|
|
@ -50,6 +50,8 @@ namespace Vanguard.UI
|
||||||
private Button _botButton;
|
private Button _botButton;
|
||||||
private Button _duelButton;
|
private Button _duelButton;
|
||||||
private Button _deleteButton;
|
private Button _deleteButton;
|
||||||
|
private Button _dismantleAllButton;
|
||||||
|
private RectTransform _unownedChips;
|
||||||
private RectTransform _deckMenuOverlay;
|
private RectTransform _deckMenuOverlay;
|
||||||
private Text _deckMenuTitle;
|
private Text _deckMenuTitle;
|
||||||
private DeckDto _deckMenuDeck;
|
private DeckDto _deckMenuDeck;
|
||||||
|
|
@ -61,8 +63,9 @@ namespace Vanguard.UI
|
||||||
private RectTransform _clanChips;
|
private RectTransform _clanChips;
|
||||||
private RectTransform _confirmOverlay;
|
private RectTransform _confirmOverlay;
|
||||||
private InputField _searchInput;
|
private InputField _searchInput;
|
||||||
|
private Text _confirmTitle;
|
||||||
private Text _confirmText;
|
private Text _confirmText;
|
||||||
private DeckDto _pendingDelete;
|
private Action _confirmAction;
|
||||||
|
|
||||||
private RectTransform _cardOverlay;
|
private RectTransform _cardOverlay;
|
||||||
private Image _cardArt;
|
private Image _cardArt;
|
||||||
|
|
@ -200,8 +203,15 @@ namespace Vanguard.UI
|
||||||
var filters = UiKit.CreateButton(filterBar, "Filters", "Фильтры", OpenFilters, 16);
|
var filters = UiKit.CreateButton(filterBar, "Filters", "Фильтры", OpenFilters, 16);
|
||||||
SetFixedSize(filters, 110f, 38f);
|
SetFixedSize(filters, 110f, 38f);
|
||||||
|
|
||||||
var reset = UiKit.CreateButton(filterBar, "ResetFilters", "Сброс", ResetFilters, 16);
|
_dismantleAllButton = UiKit.CreateButton(
|
||||||
SetFixedSize(reset, 90f, 38f);
|
filterBar,
|
||||||
|
"DismantleAll",
|
||||||
|
"Распылить всё",
|
||||||
|
ConfirmDismantleExtras,
|
||||||
|
15);
|
||||||
|
SetFixedSize(_dismantleAllButton, 200f, 38f);
|
||||||
|
SetButtonColor(_dismantleAllButton, new Color(0.30f, 0.16f, 0.08f, 0.95f));
|
||||||
|
_dismantleAllButton.gameObject.SetActive(false);
|
||||||
|
|
||||||
var scroll = UiKit.CreateGridScrollView(
|
var scroll = UiKit.CreateGridScrollView(
|
||||||
collectionArea,
|
collectionArea,
|
||||||
|
|
@ -334,7 +344,7 @@ namespace Vanguard.UI
|
||||||
var overlay = UiKit.CreatePanel(_root, "FilterOverlay", new Color(0f, 0f, 0f, 0.78f));
|
var overlay = UiKit.CreatePanel(_root, "FilterOverlay", new Color(0f, 0f, 0f, 0.78f));
|
||||||
|
|
||||||
var card = UiKit.CreateCenteredCard(
|
var card = UiKit.CreateCenteredCard(
|
||||||
overlay, "FilterCard", new Vector2(900f, 940f), PanelFill);
|
overlay, "FilterCard", new Vector2(900f, 1000f), PanelFill);
|
||||||
UiKit.AddVerticalLayout(card, 24, 10);
|
UiKit.AddVerticalLayout(card, 24, 10);
|
||||||
|
|
||||||
var title = UiKit.CreateText(card, "Title", "Фильтры коллекции", 28, TextPrimary);
|
var title = UiKit.CreateText(card, "Title", "Фильтры коллекции", 28, TextPrimary);
|
||||||
|
|
@ -345,10 +355,11 @@ namespace Vanguard.UI
|
||||||
UiKit.SetPreferredHeight(_searchInput, 44f);
|
UiKit.SetPreferredHeight(_searchInput, 44f);
|
||||||
_searchInput.onValueChanged.AddListener(value => _draftFilter.Search = value);
|
_searchInput.onValueChanged.AddListener(value => _draftFilter.Search = value);
|
||||||
|
|
||||||
|
_unownedChips = BuildFilterSection(card, "Отображение", 44f);
|
||||||
_rarityChips = BuildFilterSection(card, "Редкость", 44f);
|
_rarityChips = BuildFilterSection(card, "Редкость", 44f);
|
||||||
_gradeChips = BuildFilterSection(card, "Ранг", 44f);
|
_gradeChips = BuildFilterSection(card, "Ранг", 44f);
|
||||||
_triggerChips = BuildFilterSection(card, "Триггер", 44f);
|
_triggerChips = BuildFilterSection(card, "Триггер", 44f);
|
||||||
_clanChips = BuildFilterSection(card, "Клан", 260f, flexible: true);
|
_clanChips = BuildFilterSection(card, "Клан", 220f, flexible: true);
|
||||||
|
|
||||||
var footer = UiKit.CreatePanel(card, "Footer", new Color(0f, 0f, 0f, 0f));
|
var footer = UiKit.CreatePanel(card, "Footer", new Color(0f, 0f, 0f, 0f));
|
||||||
UiKit.SetPreferredHeight(footer, 48f);
|
UiKit.SetPreferredHeight(footer, 48f);
|
||||||
|
|
@ -433,18 +444,19 @@ namespace Vanguard.UI
|
||||||
_root, "DeleteDeckConfirm", new Color(0f, 0f, 0f, 0.78f));
|
_root, "DeleteDeckConfirm", new Color(0f, 0f, 0f, 0.78f));
|
||||||
|
|
||||||
var card = UiKit.CreateCenteredCard(
|
var card = UiKit.CreateCenteredCard(
|
||||||
overlay, "ConfirmCard", new Vector2(640f, 300f), PanelFill);
|
overlay, "ConfirmCard", new Vector2(640f, 320f), PanelFill);
|
||||||
UiKit.AddVerticalLayout(card, 30, 12);
|
UiKit.AddVerticalLayout(card, 30, 12);
|
||||||
|
|
||||||
var title = UiKit.CreateText(card, "Title", "Удалить колоду?", 28, TextPrimary);
|
_confirmTitle = UiKit.CreateText(card, "Title", "Удалить колоду?", 28, TextPrimary);
|
||||||
title.fontStyle = FontStyle.Bold;
|
_confirmTitle.fontStyle = FontStyle.Bold;
|
||||||
UiKit.SetPreferredHeight(title, 42f);
|
UiKit.SetPreferredHeight(_confirmTitle, 42f);
|
||||||
|
|
||||||
_confirmText = UiKit.CreateText(
|
_confirmText = UiKit.CreateText(
|
||||||
card, "Text", string.Empty, 19, TextMuted);
|
card, "Text", string.Empty, 19, TextMuted);
|
||||||
|
_confirmText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||||
UiKit.SetPreferredHeight(_confirmText, 70f);
|
UiKit.SetPreferredHeight(_confirmText, 70f);
|
||||||
|
|
||||||
var yes = UiKit.CreateButton(card, "Yes", "Удалить", ConfirmDelete, 20);
|
var yes = UiKit.CreateButton(card, "Yes", "Подтвердить", ConfirmAction, 20);
|
||||||
UiKit.SetPreferredHeight(yes, 48f);
|
UiKit.SetPreferredHeight(yes, 48f);
|
||||||
|
|
||||||
var no = UiKit.CreateButton(card, "No", "Отмена", CancelDelete, 20);
|
var no = UiKit.CreateButton(card, "No", "Отмена", CancelDelete, 20);
|
||||||
|
|
@ -877,8 +889,26 @@ namespace Vanguard.UI
|
||||||
ClearChildren(_collectionContent);
|
ClearChildren(_collectionContent);
|
||||||
_countTexts.Clear();
|
_countTexts.Clear();
|
||||||
|
|
||||||
var items = _collection
|
var ownedByPrinting = new Dictionary<string, CollectionItemDto>(StringComparer.Ordinal);
|
||||||
.Select(item => new { Item = item, Meta = MetaOf(item) })
|
foreach (var item in _collection)
|
||||||
|
ownedByPrinting[item.PrintingId] = item;
|
||||||
|
|
||||||
|
var source = _filter.IncludeUnowned
|
||||||
|
? _catalog.Values.Select(meta => new
|
||||||
|
{
|
||||||
|
Meta = meta,
|
||||||
|
Item = ownedByPrinting.TryGetValue(meta.PrintingId, out var owned)
|
||||||
|
? owned
|
||||||
|
: new CollectionItemDto
|
||||||
|
{
|
||||||
|
PrintingId = meta.PrintingId,
|
||||||
|
NameId = meta.NameId,
|
||||||
|
Count = 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
: _collection.Select(item => new { Meta = MetaOf(item), Item = item });
|
||||||
|
|
||||||
|
var items = source
|
||||||
.Where(pair => pair.Meta != null && _filter.Matches(pair.Meta))
|
.Where(pair => pair.Meta != null && _filter.Matches(pair.Meta))
|
||||||
.OrderBy(pair => pair.Meta.Clan, StringComparer.Ordinal)
|
.OrderBy(pair => pair.Meta.Clan, StringComparer.Ordinal)
|
||||||
.ThenBy(pair => pair.Meta.Grade)
|
.ThenBy(pair => pair.Meta.Grade)
|
||||||
|
|
@ -886,6 +916,8 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
foreach (var pair in items)
|
foreach (var pair in items)
|
||||||
AddCollectionCell(pair.Item, pair.Meta);
|
AddCollectionCell(pair.Item, pair.Meta);
|
||||||
|
|
||||||
|
UpdateDismantleButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddCollectionCell(CollectionItemDto item, CardCatalogItemDto meta)
|
private void AddCollectionCell(CollectionItemDto item, CardCatalogItemDto meta)
|
||||||
|
|
@ -899,7 +931,7 @@ namespace Vanguard.UI
|
||||||
frame.effectColor = CardArt.ClanColor(meta.Clan);
|
frame.effectColor = CardArt.ClanColor(meta.Clan);
|
||||||
frame.effectDistance = new Vector2(2f, -2f);
|
frame.effectDistance = new Vector2(2f, -2f);
|
||||||
|
|
||||||
if (_deckOpen)
|
if (_deckOpen && item.Count > 0)
|
||||||
{
|
{
|
||||||
var button = cell.gameObject.AddComponent<Button>();
|
var button = cell.gameObject.AddComponent<Button>();
|
||||||
button.targetGraphic = cell.GetComponent<Image>();
|
button.targetGraphic = cell.GetComponent<Image>();
|
||||||
|
|
@ -908,10 +940,13 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
var art = UiKit.CreatePanel(cell, "Art", Color.white);
|
var art = UiKit.CreatePanel(cell, "Art", Color.white);
|
||||||
UiKit.SetPreferredHeight(art, 234f);
|
UiKit.SetPreferredHeight(art, 234f);
|
||||||
|
var artImage = art.GetComponent<Image>();
|
||||||
CardImageService.Apply(
|
CardImageService.Apply(
|
||||||
art.GetComponent<Image>(),
|
artImage,
|
||||||
meta.ImageKey,
|
meta.ImageKey,
|
||||||
CardArt.ForCard(meta.NameId, meta.Clan));
|
CardArt.ForCard(meta.NameId, meta.Clan));
|
||||||
|
if (item.Count <= 0)
|
||||||
|
artImage.color = new Color(0.45f, 0.45f, 0.45f, 1f);
|
||||||
|
|
||||||
var qty = UiKit.CreatePanel(cell, "Qty", new Color(0.02f, 0.05f, 0.12f, 0.95f));
|
var qty = UiKit.CreatePanel(cell, "Qty", new Color(0.02f, 0.05f, 0.12f, 0.95f));
|
||||||
UiKit.SetPreferredHeight(qty, 46f);
|
UiKit.SetPreferredHeight(qty, 46f);
|
||||||
|
|
@ -943,9 +978,11 @@ namespace Vanguard.UI
|
||||||
var atCap = _deckOpen
|
var atCap = _deckOpen
|
||||||
&& (used >= 4 || (meta != null && IsTrigger(meta.Trigger) && triggerCap));
|
&& (used >= 4 || (meta != null && IsTrigger(meta.Trigger) && triggerCap));
|
||||||
pair.Value.text = CountText(item);
|
pair.Value.text = CountText(item);
|
||||||
pair.Value.color = atCap
|
pair.Value.color = item.Count <= 0
|
||||||
? new Color(1f, 0.55f, 0.45f)
|
? new Color(0.50f, 0.50f, 0.50f)
|
||||||
: new Color(0.90f, 0.86f, 0.70f);
|
: atCap
|
||||||
|
? new Color(1f, 0.55f, 0.45f)
|
||||||
|
: new Color(0.90f, 0.86f, 0.70f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1032,15 +1069,16 @@ namespace Vanguard.UI
|
||||||
RebuildFilterChips();
|
RebuildFilterChips();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetFilters()
|
|
||||||
{
|
|
||||||
_filter.Clear();
|
|
||||||
UpdateFilterSummary();
|
|
||||||
RebuildCollection();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RebuildFilterChips()
|
private void RebuildFilterChips()
|
||||||
{
|
{
|
||||||
|
ClearChildren(_unownedChips);
|
||||||
|
Chip(
|
||||||
|
_unownedChips,
|
||||||
|
"Показывать вне коллекции",
|
||||||
|
_draftFilter.IncludeUnowned,
|
||||||
|
ToggleUnowned,
|
||||||
|
360f);
|
||||||
|
|
||||||
ClearChildren(_rarityChips);
|
ClearChildren(_rarityChips);
|
||||||
foreach (var rarity in new[] { "C", "R", "RR", "RRR", "SP" })
|
foreach (var rarity in new[] { "C", "R", "RR", "RRR", "SP" })
|
||||||
{
|
{
|
||||||
|
|
@ -1130,6 +1168,12 @@ namespace Vanguard.UI
|
||||||
RebuildFilterChips();
|
RebuildFilterChips();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ToggleUnowned()
|
||||||
|
{
|
||||||
|
_draftFilter.IncludeUnowned = !_draftFilter.IncludeUnowned;
|
||||||
|
RebuildFilterChips();
|
||||||
|
}
|
||||||
|
|
||||||
private static Button Chip(
|
private static Button Chip(
|
||||||
Transform parent,
|
Transform parent,
|
||||||
string label,
|
string label,
|
||||||
|
|
@ -1174,6 +1218,8 @@ namespace Vanguard.UI
|
||||||
parts.Add("Триггер: " + string.Join(", ", _filter.Triggers.OrderBy(item => item, StringComparer.Ordinal)));
|
parts.Add("Триггер: " + string.Join(", ", _filter.Triggers.OrderBy(item => item, StringComparer.Ordinal)));
|
||||||
if (!string.IsNullOrWhiteSpace(_filter.Search))
|
if (!string.IsNullOrWhiteSpace(_filter.Search))
|
||||||
parts.Add("Поиск: " + _filter.Search.Trim());
|
parts.Add("Поиск: " + _filter.Search.Trim());
|
||||||
|
if (_filter.IncludeUnowned)
|
||||||
|
parts.Add("вне коллекции");
|
||||||
|
|
||||||
_filterSummary.text = string.Join(" · ", parts);
|
_filterSummary.text = string.Join(" · ", parts);
|
||||||
}
|
}
|
||||||
|
|
@ -1420,25 +1466,88 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
private void RequestDelete(DeckDto deck)
|
private void RequestDelete(DeckDto deck)
|
||||||
{
|
{
|
||||||
_pendingDelete = deck;
|
_confirmTitle.text = "Удалить колоду?";
|
||||||
_confirmText.text = deck.Name + "\nКолода будет удалена без восстановления.";
|
_confirmText.text = deck.Name + "\nКолода будет удалена без восстановления.";
|
||||||
|
_confirmAction = () => DeleteDeck(deck);
|
||||||
|
ShowConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfirmDismantleExtras()
|
||||||
|
{
|
||||||
|
var plan = DeckListModel.PlanExtras(_collection);
|
||||||
|
if (plan.IsEmpty || _busy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_confirmTitle.text = "Распылить лишние карты?";
|
||||||
|
_confirmText.text = "Будет распылено " + plan.CardCount
|
||||||
|
+ " карт на " + plan.Dust + " пыли.";
|
||||||
|
_confirmAction = () => DismantleExtras(plan);
|
||||||
|
ShowConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowConfirm()
|
||||||
|
{
|
||||||
_confirmOverlay.SetAsLastSibling();
|
_confirmOverlay.SetAsLastSibling();
|
||||||
_confirmOverlay.gameObject.SetActive(true);
|
_confirmOverlay.gameObject.SetActive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelDelete()
|
private void CancelDelete()
|
||||||
{
|
{
|
||||||
_pendingDelete = null;
|
_confirmAction = null;
|
||||||
_confirmOverlay.gameObject.SetActive(false);
|
_confirmOverlay.gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfirmDelete()
|
private void ConfirmAction()
|
||||||
{
|
{
|
||||||
var deck = _pendingDelete;
|
var action = _confirmAction;
|
||||||
|
_confirmAction = null;
|
||||||
_confirmOverlay.gameObject.SetActive(false);
|
_confirmOverlay.gameObject.SetActive(false);
|
||||||
_pendingDelete = null;
|
action?.Invoke();
|
||||||
if (deck != null)
|
}
|
||||||
DeleteDeck(deck);
|
|
||||||
|
private async void DismantleExtras(DismantlePlan plan)
|
||||||
|
{
|
||||||
|
if (_busy || plan == null || plan.IsEmpty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_busy = true;
|
||||||
|
UpdateDismantleButton();
|
||||||
|
SetStatus("Распыляем…", false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _auth.EnsureAccessAsync();
|
||||||
|
foreach (var item in plan.Items)
|
||||||
|
await _meta.DismantleAsync(item.PrintingId, item.Count);
|
||||||
|
|
||||||
|
await ReloadCollectionAsync(null);
|
||||||
|
SetStatus(
|
||||||
|
"Распылено " + plan.CardCount + " карт на " + plan.Dust + " пыли.",
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
catch (ApiException exception)
|
||||||
|
{
|
||||||
|
SetStatus(exception.Message, true);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Debug.LogException(exception);
|
||||||
|
SetStatus("Не удалось распылить карты.", true);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_busy = false;
|
||||||
|
UpdateDismantleButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDismantleButton()
|
||||||
|
{
|
||||||
|
if (_dismantleAllButton == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var plan = DeckListModel.PlanExtras(_collection);
|
||||||
|
_dismantleAllButton.gameObject.SetActive(!plan.IsEmpty);
|
||||||
|
_dismantleAllButton.interactable = !plan.IsEmpty && !_busy;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------ карточка
|
// ------------------------------------------------------------ карточка
|
||||||
|
|
@ -1500,7 +1609,7 @@ namespace Vanguard.UI
|
||||||
_craftButton.interactable = canChange && !_busy && _dust >= craftCost;
|
_craftButton.interactable = canChange && !_busy && _dust >= craftCost;
|
||||||
SetButtonLabel(
|
SetButtonLabel(
|
||||||
_dismantleButton,
|
_dismantleButton,
|
||||||
canChange ? "Разобрать (+" + DustFor(meta.Rarity) + " пыли)" : "Нельзя разобрать");
|
canChange ? "Разобрать (+" + DeckListModel.DustFor(meta.Rarity) + " пыли)" : "Нельзя разобрать");
|
||||||
SetButtonLabel(
|
SetButtonLabel(
|
||||||
_craftButton,
|
_craftButton,
|
||||||
canChange
|
canChange
|
||||||
|
|
@ -1586,7 +1695,12 @@ namespace Vanguard.UI
|
||||||
_dust = collection.Balances != null ? collection.Balances.Dust : 0;
|
_dust = collection.Balances != null ? collection.Balances.Dust : 0;
|
||||||
RebuildCollection();
|
RebuildCollection();
|
||||||
RefreshCounts();
|
RefreshCounts();
|
||||||
_selectedItem = _collection.Find(item => item.PrintingId == printingId);
|
UpdateDismantleButton();
|
||||||
|
|
||||||
|
_selectedItem = printingId == null
|
||||||
|
? null
|
||||||
|
: _collection.Find(item => item.PrintingId == printingId);
|
||||||
|
|
||||||
if (_selectedItem == null)
|
if (_selectedItem == null)
|
||||||
{
|
{
|
||||||
_cardOverlay.gameObject.SetActive(false);
|
_cardOverlay.gameObject.SetActive(false);
|
||||||
|
|
@ -1638,15 +1752,6 @@ namespace Vanguard.UI
|
||||||
private CardCatalogItemDto MetaOf(CollectionItemDto item) =>
|
private CardCatalogItemDto MetaOf(CollectionItemDto item) =>
|
||||||
_catalog.TryGetValue(item.PrintingId, out var meta) ? meta : null;
|
_catalog.TryGetValue(item.PrintingId, out var meta) ? meta : null;
|
||||||
|
|
||||||
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
|
private static int CraftCostFor(string rarity) => rarity switch
|
||||||
{
|
{
|
||||||
"C" => 20,
|
"C" => 20,
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,10 @@ namespace Vanguard.UI
|
||||||
public HashSet<string> Triggers { get; } = new HashSet<string>(StringComparer.Ordinal);
|
public HashSet<string> Triggers { get; } = new HashSet<string>(StringComparer.Ordinal);
|
||||||
public HashSet<string> Rarities { get; } = new HashSet<string>(StringComparer.Ordinal);
|
public HashSet<string> Rarities { get; } = new HashSet<string>(StringComparer.Ordinal);
|
||||||
public string Search { get; set; } = string.Empty;
|
public string Search { get; set; } = string.Empty;
|
||||||
|
public bool IncludeUnowned { get; set; }
|
||||||
|
|
||||||
public bool IsEmpty => Clans.Count == 0 && Grades.Count == 0 && Triggers.Count == 0
|
public bool IsEmpty => Clans.Count == 0 && Grades.Count == 0 && Triggers.Count == 0
|
||||||
&& Rarities.Count == 0 && string.IsNullOrWhiteSpace(Search);
|
&& Rarities.Count == 0 && string.IsNullOrWhiteSpace(Search) && !IncludeUnowned;
|
||||||
|
|
||||||
public bool Matches(CardCatalogItemDto meta)
|
public bool Matches(CardCatalogItemDto meta)
|
||||||
{
|
{
|
||||||
|
|
@ -54,6 +55,7 @@ namespace Vanguard.UI
|
||||||
Triggers.Clear();
|
Triggers.Clear();
|
||||||
Rarities.Clear();
|
Rarities.Clear();
|
||||||
Search = string.Empty;
|
Search = string.Empty;
|
||||||
|
IncludeUnowned = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyFrom(DeckFilter other)
|
public void CopyFrom(DeckFilter other)
|
||||||
|
|
@ -67,6 +69,7 @@ namespace Vanguard.UI
|
||||||
Triggers.UnionWith(other.Triggers);
|
Triggers.UnionWith(other.Triggers);
|
||||||
Rarities.UnionWith(other.Rarities);
|
Rarities.UnionWith(other.Rarities);
|
||||||
Search = other.Search ?? string.Empty;
|
Search = other.Search ?? string.Empty;
|
||||||
|
IncludeUnowned = other.IncludeUnowned;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeckFilter Clone()
|
public DeckFilter Clone()
|
||||||
|
|
@ -98,6 +101,35 @@ namespace Vanguard.UI
|
||||||
public bool IsValid => Count == 50 && Triggers == 16 && MaxCopiesOk;
|
public bool IsValid => Count == 50 && Triggers == 16 && MaxCopiesOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class DismantlePlanItem
|
||||||
|
{
|
||||||
|
public string PrintingId { get; set; } = string.Empty;
|
||||||
|
public int Count { get; set; }
|
||||||
|
public int Dust { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Лишние копии сверх 4 на название, которые можно распылить.</summary>
|
||||||
|
public sealed class DismantlePlan
|
||||||
|
{
|
||||||
|
public List<DismantlePlanItem> Items { get; } = new List<DismantlePlanItem>();
|
||||||
|
public int CardCount { get; private set; }
|
||||||
|
public int Dust { get; private set; }
|
||||||
|
|
||||||
|
public bool IsEmpty => Items.Count == 0;
|
||||||
|
|
||||||
|
public void Add(string printingId, int count, int dust)
|
||||||
|
{
|
||||||
|
Items.Add(new DismantlePlanItem
|
||||||
|
{
|
||||||
|
PrintingId = printingId,
|
||||||
|
Count = count,
|
||||||
|
Dust = dust,
|
||||||
|
});
|
||||||
|
CardCount += count;
|
||||||
|
Dust += dust;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Чистые правила фильтрации, группировки и проверки колоды.</summary>
|
/// <summary>Чистые правила фильтрации, группировки и проверки колоды.</summary>
|
||||||
public static class DeckListModel
|
public static class DeckListModel
|
||||||
{
|
{
|
||||||
|
|
@ -172,5 +204,48 @@ namespace Vanguard.UI
|
||||||
public static bool IsTrigger(string trigger) =>
|
public static bool IsTrigger(string trigger) =>
|
||||||
!string.IsNullOrEmpty(trigger)
|
!string.IsNullOrEmpty(trigger)
|
||||||
&& !string.Equals(trigger, "None", StringComparison.Ordinal);
|
&& !string.Equals(trigger, "None", StringComparison.Ordinal);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// План распыления лишних копий: всё сверх 4 на название. SP/TD не
|
||||||
|
/// распыляются, поэтому пропускаются. Сначала берутся карты с меньшей
|
||||||
|
/// ценой распыления, чтобы сохранить ценные.
|
||||||
|
/// </summary>
|
||||||
|
public static DismantlePlan PlanExtras(IEnumerable<CollectionItemDto> collection)
|
||||||
|
{
|
||||||
|
var plan = new DismantlePlan();
|
||||||
|
if (collection == null)
|
||||||
|
return plan;
|
||||||
|
|
||||||
|
foreach (var group in collection.GroupBy(item => item.NameId))
|
||||||
|
{
|
||||||
|
var total = group.Sum(item => item.Count);
|
||||||
|
var extra = total - 4;
|
||||||
|
if (extra <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var item in group
|
||||||
|
.Where(item => DustFor(item.Rarity) > 0 && item.Count > 0)
|
||||||
|
.OrderBy(item => DustFor(item.Rarity)))
|
||||||
|
{
|
||||||
|
if (extra <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var take = Math.Min(extra, item.Count);
|
||||||
|
plan.Add(item.PrintingId, take, take * DustFor(item.Rarity));
|
||||||
|
extra -= take;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int DustFor(string rarity) => rarity switch
|
||||||
|
{
|
||||||
|
"C" => 5,
|
||||||
|
"R" => 10,
|
||||||
|
"RR" => 25,
|
||||||
|
"RRR" => 100,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -440,6 +440,18 @@ namespace Vanguard.UI
|
||||||
["есть"] = "owned",
|
["есть"] = "owned",
|
||||||
["в колоде"] = "in deck",
|
["в колоде"] = "in deck",
|
||||||
["Открыть"] = "Open",
|
["Открыть"] = "Open",
|
||||||
|
["Распылить всё"] = "Dismantle all",
|
||||||
|
["Показывать вне коллекции"] = "Show cards not owned",
|
||||||
|
["Распылить лишние карты?"] = "Dismantle excess cards?",
|
||||||
|
["Будет распылено "] = "Will dismantle ",
|
||||||
|
[" карт на "] = " cards for ",
|
||||||
|
[" пыли."] = " dust.",
|
||||||
|
["Распыляем…"] = "Dismantling…",
|
||||||
|
["Распылено "] = "Dismantled ",
|
||||||
|
["Не удалось распылить карты."] = "Could not dismantle the cards.",
|
||||||
|
["вне коллекции"] = "not owned",
|
||||||
|
["Подтвердить"] = "Confirm",
|
||||||
|
["Отображение"] = "Display",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly KeyValuePair<string, string>[] Phrases = Exact
|
private static readonly KeyValuePair<string, string>[] Phrases = Exact
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,24 @@ namespace Vanguard.Tests
|
||||||
Assert.AreEqual("missing", groups[1].NameId);
|
Assert.AreEqual("missing", groups[1].NameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PlanExtras_Takes_Copies_Above_Four_And_Skips_SP()
|
||||||
|
{
|
||||||
|
var collection = new List<CollectionItemDto>
|
||||||
|
{
|
||||||
|
new CollectionItemDto { PrintingId = "a", NameId = "A", Rarity = "C", Count = 5 },
|
||||||
|
new CollectionItemDto { PrintingId = "b", NameId = "B", Rarity = "SP", Count = 6 },
|
||||||
|
new CollectionItemDto { PrintingId = "c", NameId = "C", Rarity = "RR", Count = 3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
var plan = DeckListModel.PlanExtras(collection);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, plan.CardCount);
|
||||||
|
Assert.AreEqual(5, plan.Dust);
|
||||||
|
Assert.AreEqual(1, plan.Items.Count);
|
||||||
|
Assert.AreEqual("a", plan.Items[0].PrintingId);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Validate_Requires_Count_Triggers_And_Copy_Limit()
|
public void Validate_Requires_Count_Triggers_And_Copy_Limit()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue