From 136db664555fe4504cc416ec90f608a901667472 Mon Sep 17 00:00:00 2001 From: MpakobecEx Date: Fri, 23 Oct 2026 19:46:05 +0500 Subject: [PATCH] Select hand cards for Morgana discard by clicking hand --- .../Assets/Vanguard/Runtime/UI/BattleView.cs | 75 ++++++++++++++++++- .../Vanguard/Tests/Editor/ClientSmokeTests.cs | 72 ++++++++++++++++++ .../src/Vanguard.Rules/Engine/GameEngine.cs | 4 +- 3 files changed, 147 insertions(+), 4 deletions(-) diff --git a/client/Assets/Vanguard/Runtime/UI/BattleView.cs b/client/Assets/Vanguard/Runtime/UI/BattleView.cs index 899d593..344c7e6 100644 --- a/client/Assets/Vanguard/Runtime/UI/BattleView.cs +++ b/client/Assets/Vanguard/Runtime/UI/BattleView.cs @@ -1385,6 +1385,51 @@ namespace Vanguard.UI && options.All(option => TryGetChoicePrintingId(option, out _)); } + private bool IsHandChoice() + { + var choice = _view?.Choice; + if (choice == null + || _view == null + || _view.ActivePlayer != _view.You + || choice.Options == null + || choice.Options.Count == 0) + return false; + + var prompt = choice.Prompt ?? string.Empty; + if (prompt.IndexOf("рук", StringComparison.OrdinalIgnoreCase) < 0 + && prompt.IndexOf("сброс", StringComparison.OrdinalIgnoreCase) < 0) + return false; + + var hand = _view.Players + .FirstOrDefault(player => player.Index == _view.You) + ?.Hand; + return hand != null + && choice.Options.All(option => option.Slot >= 0 && option.Slot < hand.Count); + } + + private void HandleHandChoiceCard(BoardCardRef card) + { + if (_view?.Choice == null || card.Zone != BoardZone.Hand || card.Player != _view.You) + { + SetStatus("Выберите карту в руке.", new Color(0.98f, 0.82f, 0.42f)); + return; + } + + var handIndex = HandIndexOf(card.InstanceId); + var optionIndex = _view.Choice.Options + .ToList() + .FindIndex(option => option.Slot == handIndex); + if (optionIndex < 0) + { + SetStatus("Эта карта недоступна для выбора.", new Color(1f, 0.55f, 0.45f)); + return; + } + + OnIntentClicked( + new ChooseIntent { Index = optionIndex }, + ignoreInputLock: true); + } + private void SyncCardChoicePrompt() { if (_cardChoiceOverlay == null || _sending) @@ -1663,6 +1708,21 @@ namespace Vanguard.UI } } + if (IsHandChoice()) + { + var hand = _view.Players + .FirstOrDefault(player => player.Index == _view.You) + ?.Hand; + if (hand != null) + { + foreach (var option in _view.Choice.Options) + { + if (option.Slot >= 0 && option.Slot < hand.Count) + state.SelectedHandInstanceIds.Add(hand[option.Slot].InstanceId); + } + } + } + if (state.MulliganSelecting && _view != null && _mulliganSelected.Count > 0) { var hand = _view.Players @@ -1722,7 +1782,7 @@ namespace Vanguard.UI private void OnBoard3DCardClicked(BoardCardRef card) { - if (_view == null || _sending || _inputLock.IsLocked) + if (_view == null || _sending) return; if (_discardChoiceActive) @@ -1731,6 +1791,15 @@ namespace Vanguard.UI return; } + if (IsHandChoice()) + { + HandleHandChoiceCard(card); + return; + } + + if (_inputLock.IsLocked) + return; + var targetSelection = _pendingIntents.Count > 0 || IsMulliganSelecting() || IsDamageChoice() @@ -2528,7 +2597,7 @@ namespace Vanguard.UI { title = IsRearGuardChoice() ? "ВЫБОР КРУГА" - : IsCardChoice() ? "ВЫБОР КАРТЫ" : "ВЫБОР"; + : IsHandChoice() || IsCardChoice() ? "ВЫБОР КАРТЫ" : "ВЫБОР"; hint = _view.Choice.Prompt; } else if (_view.ActivePlayer != _view.You) @@ -3299,7 +3368,7 @@ namespace Vanguard.UI && intent is not ActivateAbilityIntent && intent is not ResolveAbilityIntent && !(intent is ChooseIntent - && (IsRearGuardChoice() || IsCardChoice()))) + && (IsRearGuardChoice() || IsHandChoice() || IsCardChoice()))) .ToList(); if (_pendingIntents.Count > 0) diff --git a/client/Assets/Vanguard/Tests/Editor/ClientSmokeTests.cs b/client/Assets/Vanguard/Tests/Editor/ClientSmokeTests.cs index 7a83c8c..078b35c 100644 --- a/client/Assets/Vanguard/Tests/Editor/ClientSmokeTests.cs +++ b/client/Assets/Vanguard/Tests/Editor/ClientSmokeTests.cs @@ -437,6 +437,78 @@ namespace Vanguard.Tests } } + [Test] + public void Hand_Choice_Uses_Hand_Cards_Without_Intent_Buttons() + { + var config = new AppConfig(); + config.Load(); + var tokens = new TokenStore(); + var api = new ApiClient(config, tokens); + var player = new PlayerState(); + var auth = new AuthService(api, tokens, player); + var meta = new MetaService(api, player); + var canvas = UiKit.CreateCanvas("Hand Choice Test Canvas"); + + try + { + var battle = new BattleView(canvas.transform, meta, () => { }); + var match = new MatchView + { + You = 0, + ActivePlayer = 0, + Players = new List + { + new() + { + Index = 0, + Hand = new List + { + new() { InstanceId = 11, PrintingId = "BT01/046EN" }, + new() { InstanceId = 12, PrintingId = "BT01/042EN" }, + }, + }, + }, + Choice = new ChoiceView + { + Player = 0, + Prompt = "Выберите карту из руки", + Options = new List + { + new() { Slot = 0, Label = "Flogal" }, + new() { Slot = 1, Label = "Little Sage, Marron" }, + }, + }, + LegalIntents = new List(), + }; + typeof(BattleView) + .GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)! + .SetValue(battle, match); + + var isHandChoice = typeof(BattleView) + .GetMethod("IsHandChoice", BindingFlags.Instance | BindingFlags.NonPublic)! + .Invoke(battle, null); + Assert.AreEqual(true, isHandChoice); + + typeof(BattleView) + .GetMethod("RefreshActionPrompt", BindingFlags.Instance | BindingFlags.NonPublic)! + .Invoke(battle, null); + var title = Resources.FindObjectsOfTypeAll() + .First(text => text.name == "ActionPromptTitle"); + Assert.AreEqual("ВЫБОР КАРТЫ", title.text); + + typeof(BattleView) + .GetMethod("RebuildIntents", BindingFlags.Instance | BindingFlags.NonPublic)! + .Invoke(battle, new object[] { match }); + Assert.IsFalse(Resources.FindObjectsOfTypeAll() + .Any(go => go.name == "Intent_ChooseIntent")); + } + finally + { + Object.DestroyImmediate(canvas.gameObject); + Cleanup(); + } + } + [Test] public void Action_Prompt_Shows_Current_Required_Action() { diff --git a/server/src/Vanguard.Rules/Engine/GameEngine.cs b/server/src/Vanguard.Rules/Engine/GameEngine.cs index 163681a..f086cde 100644 --- a/server/src/Vanguard.Rules/Engine/GameEngine.cs +++ b/server/src/Vanguard.Rules/Engine/GameEngine.cs @@ -1140,7 +1140,9 @@ public sealed class GameEngine Player = player, Prompt = prompt, Options = cards - .Select((card, index) => new ChoiceOption(index, State.Catalog.CardOf(card.PrintingId).NameEn)) + .Select(card => new ChoiceOption( + State.Players[player].Hand.IndexOf(card), + State.Catalog.CardOf(card.PrintingId).NameEn)) .ToList(), Resolve = index => onChosen(cards[index]), };