diff --git a/IMPLEMENTATION_CHECKLIST.md b/IMPLEMENTATION_CHECKLIST.md index a83882f..8a356bd 100644 --- a/IMPLEMENTATION_CHECKLIST.md +++ b/IMPLEMENTATION_CHECKLIST.md @@ -279,7 +279,8 @@ сила/крит; REST/скрытая карта. - [x] Старт: маллиган (сервер пока даёт только «закончить маллиган»). - [x] Ввод: кнопки легальных интентов; клик по картам/зонам, - подсветка целей, группировка действий и журнал боя. + подсветка целей, группировка действий, выбор силы/эффекта триггера + и журнал боя. - [x] Boost: выбор карты заднего ряда как поддержки перед атакой. - [x] Event-фидбек: атака, guard, drive, урон, heal, retire, perfect guard. - [x] Отображение силы/крита из `MatchView`; модификаторы уже посчитаны сервером. diff --git a/client/Assets/Vanguard/Runtime/UI/BattleView.cs b/client/Assets/Vanguard/Runtime/UI/BattleView.cs index 0de2c94..a41f5c5 100644 --- a/client/Assets/Vanguard/Runtime/UI/BattleView.cs +++ b/client/Assets/Vanguard/Runtime/UI/BattleView.cs @@ -69,6 +69,7 @@ namespace Vanguard.UI private readonly List _pendingIntents = new List(); private readonly HashSet _highlightKeys = new HashSet(StringComparer.Ordinal); private int _pendingBoosterSlot = -1; + private int? _triggerPowerSlot; public BattleView(Transform parent, MetaService meta, Action onExit) { @@ -187,6 +188,7 @@ namespace Vanguard.UI _pendingIntents.Clear(); _highlightKeys.Clear(); _pendingBoosterSlot = -1; + _triggerPowerSlot = null; _showAllIntents = false; _logLines.Clear(); RebuildLog(); @@ -432,7 +434,8 @@ namespace Vanguard.UI _finished = _view.IsFinished; _pendingIntents.Clear(); _highlightKeys.Clear(); - _pendingBoosterSlot = -1; + _triggerPowerSlot = null; + PrepareTriggerSelection(); if (_view.Clock != null) { _clockServerMs = _view.Clock.ServerTimeMs; @@ -624,6 +627,7 @@ namespace Vanguard.UI _pendingIntents.Clear(); _highlightKeys.Clear(); _pendingBoosterSlot = -1; + _triggerPowerSlot = null; _sending = true; SetStatus("Отправляем: " + Label(intent), Muted); @@ -1115,6 +1119,12 @@ namespace Vanguard.UI var legal = _view.LegalIntents ?? new List(); + if (_pendingIntents.Any(intent => intent is ResolveTriggerIntent) + && HandleTriggerTarget(target)) + { + return; + } + if (_pendingIntents.Count > 0) { // Клик по своему заднему ряду выбирает карту поддержки. @@ -1199,13 +1209,84 @@ namespace Vanguard.UI _pendingIntents.Clear(); _highlightKeys.Clear(); _pendingBoosterSlot = -1; + _triggerPowerSlot = null; if (rebuild && _view != null) RebuildIntents(_view); } + private void PrepareTriggerSelection() + { + if (_view?.Trigger == null || _view.ActivePlayer != _view.You) + return; + + var triggers = (_view.LegalIntents ?? new List()) + .OfType() + .ToList(); + if (triggers.Count == 0) + return; + + _pendingIntents.Clear(); + _pendingIntents.AddRange(triggers); + _triggerPowerSlot = null; + RebuildHighlights(); + } + + private bool HandleTriggerTarget(BoardTarget target) + { + if (_view?.Trigger == null || target.Player != _view.You) + return false; + + var triggers = _pendingIntents.OfType().ToList(); + if (triggers.Count == 0) + return false; + + if (_triggerPowerSlot == null) + { + var powerTargets = triggers + .Where(trigger => trigger.PowerTarget == target.Slot) + .ToList(); + if (powerTargets.Count == 0) + return false; + + _triggerPowerSlot = target.Slot; + _pendingIntents.Clear(); + _pendingIntents.AddRange(powerTargets); + RebuildHighlights(); + RebuildIntents(_view); + SetStatus( + "Триггер: выберите цель эффекта.", + new Color(0.98f, 0.82f, 0.42f)); + return true; + } + + var effect = triggers.FirstOrDefault(trigger => trigger.EffectTarget == target.Slot); + if (effect == null) + return false; + + ClearSelection(rebuild: false); + OnIntentClicked(effect); + return true; + } + private void RebuildHighlights() { _highlightKeys.Clear(); + + if (_pendingIntents.Any(intent => intent is ResolveTriggerIntent)) + { + foreach (var trigger in _pendingIntents.OfType()) + { + var slot = _triggerPowerSlot == null + ? trigger.PowerTarget + : trigger.EffectTarget; + var card = CardAt(_view.You, slot); + _highlightKeys.Add(TargetKey( + _view.You, slot, card != null ? card.PrintingId : null)); + } + + return; + } + foreach (var intent in _pendingIntents) { if (TryGetTarget(intent, out var target)) @@ -1358,11 +1439,15 @@ namespace Vanguard.UI var prompt = UiKit.CreateText( _intents, "TargetPrompt", - _pendingBoosterSlot >= 0 - ? "Поддержка: " + CardName( - CardAt(_view.You, _pendingBoosterSlot)?.PrintingId ?? string.Empty) - + ".\nВыберите цель атаки или «Отмена»." - : "Выберите цель на поле.\nНажмите «Отмена», чтобы снять выбор.", + _pendingIntents.Any(intent => intent is ResolveTriggerIntent) + ? (_triggerPowerSlot == null + ? "Триггер: выберите цель силы." + : "Триггер: выберите цель эффекта.") + : _pendingBoosterSlot >= 0 + ? "Поддержка: " + CardName( + CardAt(_view.You, _pendingBoosterSlot)?.PrintingId ?? string.Empty) + + ".\nВыберите цель атаки или «Отмена»." + : "Выберите цель на поле.\nНажмите «Отмена», чтобы снять выбор.", 16, new Color(0.98f, 0.82f, 0.42f), TextAnchor.UpperLeft); diff --git a/client/README.md b/client/README.md index 550170a..ecad6b6 100644 --- a/client/README.md +++ b/client/README.md @@ -65,7 +65,8 @@ http://localhost:5080 - Боевой gray-box: доска, рука, часы, кнопки всех легальных интентов, сдача с подтверждением и экран результата. - Боевой UX: клик по картам/зонам, подсветка целей, сгруппированные - действия, журнал событий и подсказки; event-анимации с очередью и скипом. + действия, выбор силы/эффекта триггера, журнал событий и подсказки; + event-анимации с очередью и скипом. - Карты в бою и коллекции: процедурные арты по клану, рамка, имя, ранг, триггер, сила/крит, REST и рубашка для скрытых карт. - Реальные арты: `CardImageService` ищет `Resources/CardArt/{imageKey}`; diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 56b6cba..0260ef6 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -756,7 +756,8 @@ Battle; Boot и общие сервисы остаются персистент - Event-фидбек: всплывающие атака/guard/drive/урон/heal/retire, не блокирует ввод; очередь событий и кнопка «Пропустить анимации». - Боевой UX: клик по карте/зоне, подсветка целей, отдельная группировка - действий, журнал последних событий и подсказки выбора. + действий, выбор силы/эффекта триггера, журнал последних событий и + подсказки выбора. - Поле: `R V R` / `R R R` у каждого игрока, зона урона слева, колода и дроп справа, зеркально у противника, зона защиты между полями. - Проверки визуального блока: Unity EditMode 2/2, `SCENE_SMOKE success=True`, diff --git a/docs/SESSION-STATE.md b/docs/SESSION-STATE.md index 7b4cb7a..515e933 100644 --- a/docs/SESSION-STATE.md +++ b/docs/SESSION-STATE.md @@ -67,7 +67,7 @@ `tools/import_card_art.py`. - Взаимодействие в бою: клик по картам/зонам, подсветка целей, группировка действий (карты → цели, глобальные кнопки отдельно), - журнал последних событий. + выбор силы/эффекта триггера, журнал последних событий. - Отдельная сцена `Collection` и `CollectionView` удалены; коллекция открывается из Lobby кнопкой «Коллекция» (сцена `DeckBuilder`). - Не готово: файлы реальных артов (пайплайн готов), 3D-модели,