Restore drag actions and hand action menu
CI / test (push) Successful in 2m5s
Details
CI / test (push) Successful in 2m5s
Details
This commit is contained in:
parent
14717a3dff
commit
d917448a5f
|
|
@ -12,8 +12,9 @@ namespace Vanguard.Board3D
|
||||||
|
|
||||||
public static bool CanCall(int slot, bool occupied) =>
|
public static bool CanCall(int slot, bool occupied) =>
|
||||||
slot >= 0
|
slot >= 0
|
||||||
&& slot < PlaymatLayout.RearGuardCount
|
&& slot < PlaymatLayout.RearGuardCount;
|
||||||
&& !occupied;
|
// Occupied circles are valid call targets: the previous unit is
|
||||||
|
// moved to the drop zone.
|
||||||
|
|
||||||
public static bool CanGuard(BoardZone zone) =>
|
public static bool CanGuard(BoardZone zone) =>
|
||||||
zone == BoardZone.Guardian;
|
zone == BoardZone.Guardian;
|
||||||
|
|
|
||||||
|
|
@ -1724,6 +1724,12 @@ namespace Vanguard.UI
|
||||||
state.DropTargets.Add(new BoardSlotKey(
|
state.DropTargets.Add(new BoardSlotKey(
|
||||||
1 - _view.You,
|
1 - _view.You,
|
||||||
attack.TargetSlot));
|
attack.TargetSlot));
|
||||||
|
if (attack.BoosterSlot >= 0)
|
||||||
|
{
|
||||||
|
state.DropTargets.Add(new BoardSlotKey(
|
||||||
|
_view.You,
|
||||||
|
attack.AttackerSlot));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GuardIntent _:
|
case GuardIntent _:
|
||||||
case InterceptIntent _:
|
case InterceptIntent _:
|
||||||
|
|
@ -1917,6 +1923,12 @@ namespace Vanguard.UI
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!targetSelection)
|
||||||
|
{
|
||||||
|
OpenCardMenu(card);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
OnBoardTargetClicked(new BoardTarget(
|
OnBoardTargetClicked(new BoardTarget(
|
||||||
card.Player, -2, card.PrintingId, card.InstanceId, true));
|
card.Player, -2, card.PrintingId, card.InstanceId, true));
|
||||||
break;
|
break;
|
||||||
|
|
@ -2191,17 +2203,6 @@ namespace Vanguard.UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasActivatedAbility(string printingId)
|
|
||||||
{
|
|
||||||
var meta = ResolveCatalogItem(printingId);
|
|
||||||
if (meta == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var text = ((meta.TextEn ?? string.Empty) + " "
|
|
||||||
+ (meta.TextRu ?? string.Empty)).ToLowerInvariant();
|
|
||||||
return text.Contains("[act]") || text.Contains("{act}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool RequiresCounterBlast(ActivateAbilityIntent intent)
|
private bool RequiresCounterBlast(ActivateAbilityIntent intent)
|
||||||
{
|
{
|
||||||
if (intent == null || _view == null)
|
if (intent == null || _view == null)
|
||||||
|
|
@ -2339,7 +2340,7 @@ namespace Vanguard.UI
|
||||||
card.Zone == BoardZone.Vanguard ? -1 : card.Slot,
|
card.Zone == BoardZone.Vanguard ? -1 : card.Slot,
|
||||||
card.PrintingId,
|
card.PrintingId,
|
||||||
card.InstanceId,
|
card.InstanceId,
|
||||||
false);
|
card.Zone == BoardZone.Hand);
|
||||||
|
|
||||||
var legal = _view.LegalIntents ?? new List<IntentPayload>();
|
var legal = _view.LegalIntents ?? new List<IntentPayload>();
|
||||||
var sources = legal
|
var sources = legal
|
||||||
|
|
@ -2371,9 +2372,26 @@ namespace Vanguard.UI
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var calls = sources.OfType<CallIntent>().ToList();
|
||||||
|
if (calls.Count > 0)
|
||||||
|
{
|
||||||
|
actions.Add(new CardActionMenuItem("Призвать", () =>
|
||||||
|
{
|
||||||
|
BeginTargetSelection(calls, "Выберите круг для призыва.");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
var rides = sources.OfType<RideIntent>().ToList();
|
||||||
|
if (rides.Count > 0)
|
||||||
|
{
|
||||||
|
var ride = rides[0];
|
||||||
|
actions.Add(new CardActionMenuItem(
|
||||||
|
"Сменить: " + CardName(ride.PrintingId),
|
||||||
|
() => OnIntentClicked(ride)));
|
||||||
|
}
|
||||||
|
|
||||||
var abilities = sources.OfType<ActivateAbilityIntent>().ToList();
|
var abilities = sources.OfType<ActivateAbilityIntent>().ToList();
|
||||||
if (abilities.Count == 0
|
if (abilities.Count == 0)
|
||||||
&& card.Zone is BoardZone.Vanguard or BoardZone.RearGuard)
|
|
||||||
{
|
{
|
||||||
// Fallback: never hide an activated ability because source
|
// Fallback: never hide an activated ability because source
|
||||||
// target resolution failed for an unusual card placement.
|
// target resolution failed for an unusual card placement.
|
||||||
|
|
@ -2391,7 +2409,8 @@ namespace Vanguard.UI
|
||||||
|
|
||||||
foreach (var intent in sources)
|
foreach (var intent in sources)
|
||||||
{
|
{
|
||||||
if (intent is AttackIntent or MoveIntent or ActivateAbilityIntent)
|
if (intent is AttackIntent or MoveIntent or ActivateAbilityIntent
|
||||||
|
or CallIntent or RideIntent)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var captured = intent;
|
var captured = intent;
|
||||||
|
|
@ -3256,22 +3275,6 @@ namespace Vanguard.UI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var ride in (view.LegalIntents ?? new List<IntentPayload>())
|
|
||||||
.OfType<RideIntent>()
|
|
||||||
.GroupBy(item => item.PrintingId)
|
|
||||||
.Select(group => group.First()))
|
|
||||||
{
|
|
||||||
var captured = ride;
|
|
||||||
var rideButton = UiKit.CreateButton(
|
|
||||||
_phasePanel,
|
|
||||||
"Phase_Ride_" + captured.PrintingId,
|
|
||||||
"Сменить: " + CardName(captured.PrintingId),
|
|
||||||
() => OnIntentClicked(captured),
|
|
||||||
14);
|
|
||||||
rideButton.interactable = !_inputLock.IsLocked;
|
|
||||||
UiKit.SetPreferredHeight(rideButton, 40f);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var intent in intents)
|
foreach (var intent in intents)
|
||||||
{
|
{
|
||||||
var captured = intent;
|
var captured = intent;
|
||||||
|
|
@ -3595,25 +3598,6 @@ namespace Vanguard.UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.IsHand
|
|
||||||
&& target.Player == _view.You
|
|
||||||
&& _pendingIntents.Count == 0
|
|
||||||
&& HasActivatedAbility(target.PrintingId))
|
|
||||||
{
|
|
||||||
var handIndex = HandIndexOf(target.InstanceId);
|
|
||||||
var hand = _view.Players
|
|
||||||
.FirstOrDefault(player => player.Index == _view.You)
|
|
||||||
?.Hand;
|
|
||||||
if (hand != null && handIndex >= 0 && handIndex < hand.Count)
|
|
||||||
{
|
|
||||||
OpenCardDetails(BoardCardRef.Hand(
|
|
||||||
_view.You,
|
|
||||||
handIndex,
|
|
||||||
hand[handIndex]));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var legal = _view.LegalIntents ?? new List<IntentPayload>();
|
var legal = _view.LegalIntents ?? new List<IntentPayload>();
|
||||||
|
|
||||||
if (_pendingIntents.Any(intent => intent is ResolveTriggerIntent)
|
if (_pendingIntents.Any(intent => intent is ResolveTriggerIntent)
|
||||||
|
|
@ -3869,6 +3853,21 @@ namespace Vanguard.UI
|
||||||
return true;
|
return true;
|
||||||
case ActivateAbilityIntent activate:
|
case ActivateAbilityIntent activate:
|
||||||
source = InstanceTarget(_view.You, activate.InstanceId);
|
source = InstanceTarget(_view.You, activate.InstanceId);
|
||||||
|
if (source.InstanceId == 0)
|
||||||
|
{
|
||||||
|
var hand = _view.Players
|
||||||
|
.FirstOrDefault(player => player.Index == _view.You)
|
||||||
|
?.Hand;
|
||||||
|
var handCard = hand?.FirstOrDefault(
|
||||||
|
card => card.InstanceId == activate.InstanceId);
|
||||||
|
if (handCard != null)
|
||||||
|
{
|
||||||
|
source = new BoardTarget(
|
||||||
|
_view.You, -2, handCard.PrintingId,
|
||||||
|
handCard.InstanceId, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return source.InstanceId != 0;
|
return source.InstanceId != 0;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -3973,7 +3972,6 @@ namespace Vanguard.UI
|
||||||
var legal = view.LegalIntents ?? new List<IntentPayload>();
|
var legal = view.LegalIntents ?? new List<IntentPayload>();
|
||||||
var playable = legal.Where(intent => intent is not ConcedeIntent).ToList();
|
var playable = legal.Where(intent => intent is not ConcedeIntent).ToList();
|
||||||
var abilities = playable.OfType<ActivateAbilityIntent>().ToList();
|
var abilities = playable.OfType<ActivateAbilityIntent>().ToList();
|
||||||
var rides = playable.OfType<RideIntent>().ToList();
|
|
||||||
var board = playable
|
var board = playable
|
||||||
.Where(intent => IsBoardIntent(intent)
|
.Where(intent => IsBoardIntent(intent)
|
||||||
&& intent is not ActivateAbilityIntent
|
&& intent is not ActivateAbilityIntent
|
||||||
|
|
@ -4086,9 +4084,6 @@ namespace Vanguard.UI
|
||||||
if (abilities.Count > 0 && _pendingAbilityCost == null)
|
if (abilities.Count > 0 && _pendingAbilityCost == null)
|
||||||
AddAbilityButton(abilities);
|
AddAbilityButton(abilities);
|
||||||
|
|
||||||
if (rides.Count > 0)
|
|
||||||
AddRideButtons(rides);
|
|
||||||
|
|
||||||
foreach (var intent in global)
|
foreach (var intent in global)
|
||||||
AddIntentButton(intent);
|
AddIntentButton(intent);
|
||||||
|
|
||||||
|
|
@ -4254,7 +4249,10 @@ namespace Vanguard.UI
|
||||||
{
|
{
|
||||||
return IsHand && other.IsHand
|
return IsHand && other.IsHand
|
||||||
&& Player == other.Player
|
&& Player == other.Player
|
||||||
&& string.Equals(PrintingId, other.PrintingId, StringComparison.Ordinal);
|
&& string.Equals(PrintingId, other.PrintingId, StringComparison.Ordinal)
|
||||||
|
&& (InstanceId == 0
|
||||||
|
|| other.InstanceId == 0
|
||||||
|
|| InstanceId == other.InstanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Player == other.Player && Slot == other.Slot;
|
return Player == other.Player && Slot == other.Slot;
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ namespace Vanguard.Tests
|
||||||
{
|
{
|
||||||
Assert.IsTrue(BoardPlacementRules.CanCall(0, occupied: false));
|
Assert.IsTrue(BoardPlacementRules.CanCall(0, occupied: false));
|
||||||
Assert.IsTrue(BoardPlacementRules.CanCall(4, occupied: false));
|
Assert.IsTrue(BoardPlacementRules.CanCall(4, occupied: false));
|
||||||
Assert.IsFalse(BoardPlacementRules.CanCall(0, occupied: true));
|
Assert.IsTrue(BoardPlacementRules.CanCall(0, occupied: true));
|
||||||
Assert.IsFalse(BoardPlacementRules.CanCall(5, occupied: false));
|
Assert.IsFalse(BoardPlacementRules.CanCall(5, occupied: false));
|
||||||
|
|
||||||
Assert.IsTrue(BoardPlacementRules.CanRide(BoardZone.Vanguard, -1));
|
Assert.IsTrue(BoardPlacementRules.CanRide(BoardZone.Vanguard, -1));
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,13 @@
|
||||||
Урон больше не подсвечивается вне режима оплаты. Правый клик отменяет выбор.
|
Урон больше не подсвечивается вне режима оплаты. Правый клик отменяет выбор.
|
||||||
- Призыв в занятый RC заменяет юнита: прежний уходит в Дроп; правило
|
- Призыв в занятый RC заменяет юнита: прежний уходит в Дроп; правило
|
||||||
распространено на призыв из руки, души и колоды. Сервер задеплоен.
|
распространено на призыв из руки, души и колоды. Сервер задеплоен.
|
||||||
- Клик по карте в руке с `[ACT]`/`{act}` открывает оверлей карты.
|
- Клик по карте в руке открывает меню действий с валидными пунктами
|
||||||
- Тесты: сервер **190/190**, клиент **38/38** EditMode; `SCENE_SMOKE` и
|
(Призвать / Сменить / Активировать способность / Подробнее); способность
|
||||||
`PLAYMAT_SMOKE` успешны.
|
в руке (Gancelot) доступна как `FromHand`-активация. Списки всех смен
|
||||||
|
убраны из панелей, смена/призыв — драгом.
|
||||||
|
- Защита из руки ограничена рангом авангарда.
|
||||||
|
- Тесты: сервер **146/146** правил + **45/45** серверных, клиент
|
||||||
|
**38/38** EditMode; `SCENE_SMOKE` и `PLAYMAT_SMOKE` успешны.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -60,7 +64,7 @@ MVP: бустер **BT01** + триал-деки **TD01/TD02** — 94 карты
|
||||||
|
|
||||||
| Проверка | Результат |
|
| Проверка | Результат |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Тесты сервера | **190/190** (145 правил/контрактов + 45 сервер) |
|
| Тесты сервера | **191/191** (146 правил/контрактов + 45 сервер) |
|
||||||
| `validate` | 0 ошибок, 0 предупреждений |
|
| `validate` | 0 ошибок, 0 предупреждений |
|
||||||
| `translations check` | 94/94 |
|
| `translations check` | 94/94 |
|
||||||
| `stress 1000` | 1000/1000 партий, 0 ошибок |
|
| `stress 1000` | 1000/1000 партий, 0 ошибок |
|
||||||
|
|
@ -93,7 +97,7 @@ dotnet run --project server\src\Vanguard.Cli -- stress 1000
|
||||||
Ожидаемо:
|
Ожидаемо:
|
||||||
|
|
||||||
```
|
```
|
||||||
Пройден! : не пройдено 0, пройдено 145 (Vanguard.Rules.Tests)
|
Пройден! : не пройдено 0, пройдено 146 (Vanguard.Rules.Tests)
|
||||||
Пройден! : не пройдено 0, пройдено 45 (Vanguard.Server.Tests)
|
Пройден! : не пройдено 0, пройдено 45 (Vanguard.Server.Tests)
|
||||||
OK: ошибок 0, предупреждений 0
|
OK: ошибок 0, предупреждений 0
|
||||||
Переведено: 94/94
|
Переведено: 94/94
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
матчмейкинг, WebSocket-матч, история, реплеи.
|
матчмейкинг, WebSocket-матч, история, реплеи.
|
||||||
- Orleans включается `Orleans:Enabled`; в compose включён. Один silo,
|
- Orleans включается `Orleans:Enabled`; в compose включён. Один silo,
|
||||||
in-process реестр соединений.
|
in-process реестр соединений.
|
||||||
- Тесты: **145** правил + **45** серверных, CI зелёный.
|
- Тесты: **146** правил + **45** серверных, CI зелёный.
|
||||||
- Стенд: `http://192.168.31.158:18080`, `/health` → ok.
|
- Стенд: `http://192.168.31.158:18080`, `/health` → ok.
|
||||||
- Диагностика Ride в `MatchGrain`: входящие интенты, результат, события,
|
- Диагностика Ride в `MatchGrain`: входящие интенты, результат, события,
|
||||||
снапшоты и `rideReady` по руке/рангам; логи через `docker compose logs`.
|
снапшоты и `rideReady` по руке/рангам; логи через `docker compose logs`.
|
||||||
|
|
@ -87,21 +87,26 @@
|
||||||
`P/C`.
|
`P/C`.
|
||||||
- Смена: в фазе Ride доступны карты того же ранга и на 1 выше. На первом
|
- Смена: в фазе Ride доступны карты того же ранга и на 1 выше. На первом
|
||||||
ходу переход в Battle не предлагается; атака по-прежнему запрещена.
|
ходу переход в Battle не предлагается; атака по-прежнему запрещена.
|
||||||
Кнопки «Сменить: карта» есть и в панели действий, и в панели «Действия».
|
Смена — драг карты из руки на VC. Списки всех возможных смен в панелях
|
||||||
- Призыв: разрешён и в занятый RC; прежний юнит уходит в Дроп. Правило
|
убраны; действия доступны из меню выбранной карты в руке.
|
||||||
|
- Призыв: клик по карте в руке открывает меню действий (Призвать / Сменить /
|
||||||
|
Активировать способность / Подробнее), drag в RC/VC выполняет призыв/смену.
|
||||||
|
Призыв разрешён и в занятый RC; прежний юнит уходит в Дроп. Правило
|
||||||
действует для обычного призыва, призыва из души/колоды и выбора круга
|
действует для обычного призыва, призыва из души/колоды и выбора круга
|
||||||
способностями.
|
способностями.
|
||||||
- Способности: кнопка «Активировать способность» одна на карту; при
|
- Способности: кнопка «Активировать способность» одна на карту; при
|
||||||
нескольких вариантах открывается overlay выбора способности. Карта для
|
нескольких вариантах открывается overlay выбора способности. Карта для
|
||||||
сброса выбирается **кликом по карте в руке**, а не пунктом меню.
|
сброса выбирается **кликом по карте в руке**, а не пунктом меню.
|
||||||
Perfect Guard также просит выбрать карту сброса в руке. Клик по карте в
|
Perfect Guard также просит выбрать карту сброса в руке. Клик по карте в
|
||||||
руке с активируемой способностью открывает оверлей карты, не призывая её.
|
руке с активируемой способностью открывает меню действий; пункт активации
|
||||||
|
показывается, если фаза и условия позволяют способность.
|
||||||
- Оплата способности: выбранные карты цены подсвечиваются в руке, а для
|
- Оплата способности: выбранные карты цены подсвечиваются в руке, а для
|
||||||
Counter Blast — доступные и выбранные карты в зоне урона. Подтверждение
|
Counter Blast — доступные и выбранные карты в зоне урона. Подтверждение
|
||||||
оплаты — кнопка «Подтвердить оплату» в панели «Действия»; правый клик
|
оплаты — кнопка «Подтвердить оплату» в панели «Действия»; правый клик
|
||||||
отменяет выбор.
|
отменяет выбор.
|
||||||
- Защита: в панели «Действия» одна кнопка — «Не защищаться» без выбора и
|
- Защита: в панели «Действия» одна кнопка — «Не защищаться» без выбора и
|
||||||
«Защищаться» с выбранными картами. Карты защиты и перехвата выбираются
|
«Защищаться» с выбранными картами. Защитник из руки должен быть не выше
|
||||||
|
ранга своего авангарда. Карты защиты и перехвата выбираются
|
||||||
кликом/драгом по объектам; допустимые подсвечиваются, выбранные — зелёным.
|
кликом/драгом по объектам; допустимые подсвечиваются, выбранные — зелёным.
|
||||||
Щит выбранных карт сразу учитывается в счётчике; после подтверждения карты
|
Щит выбранных карт сразу учитывается в счётчике; после подтверждения карты
|
||||||
уходят в зону защиты, шаг защиты завершается. Дубли карт в Guardian
|
уходят в зону защиты, шаг защиты завершается. Дубли карт в Guardian
|
||||||
|
|
|
||||||
|
|
@ -185,9 +185,10 @@ G-эра, в Classic не используется: выбор воина нас
|
||||||
уходит в rest. Цель — воин соперника в переднем ряду (или авангард).
|
уходит в rest. Цель — воин соперника в переднем ряду (или авангард).
|
||||||
2. **Boost.** Стоящий воин 0–1 ранга позади атакующего может уйти в rest;
|
2. **Boost.** Стоящий воин 0–1 ранга позади атакующего может уйти в rest;
|
||||||
его сила добавляется атакующему до конца битвы.
|
его сила добавляется атакующему до конца битвы.
|
||||||
3. **Guard.** Защищающийся может призвать защитников из руки. Щит
|
3. **Guard.** Защищающийся может призвать защитников из руки. Ранг защитника
|
||||||
защитников/перехватчиков добавляется к силе защищающегося до конца битвы.
|
не выше ранга своего авангарда. Щит защитников/перехватчиков добавляется к
|
||||||
У воинов 3+ ранга щита нет, кроме стражей наследия.
|
силе защищающегося до конца битвы. У воинов 3+ ранга щита нет, кроме
|
||||||
|
стражей наследия.
|
||||||
4. **Intercept.** Воин 2 ранга из переднего ряда арьергарда может выйти на
|
4. **Intercept.** Воин 2 ранга из переднего ряда арьергарда может выйти на
|
||||||
перехват; его щит учитывается, после боя он уходит в дроп.
|
перехват; его щит учитывается, после боя он уходит в дроп.
|
||||||
5. **Drive Check.** Только если атакующий — авангард. Верхняя карта колоды
|
5. **Drive Check.** Только если атакующий — авангард. Верхняя карта колоды
|
||||||
|
|
|
||||||
|
|
@ -1537,9 +1537,13 @@ public sealed class GameEngine
|
||||||
if (State.Step != Step.Guard || State.Battle is null)
|
if (State.Step != Step.Guard || State.Battle is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var defenderGrade = VanguardGrade(1 - State.Battle.AttackerPlayer);
|
||||||
foreach (var printingId in p.Hand.Select(c => c.PrintingId).Distinct())
|
foreach (var printingId in p.Hand.Select(c => c.PrintingId).Distinct())
|
||||||
{
|
{
|
||||||
var def = State.Catalog.CardOf(printingId);
|
var def = State.Catalog.CardOf(printingId);
|
||||||
|
if (def.Grade > defenderGrade)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (def.IsSentinel)
|
if (def.IsSentinel)
|
||||||
{
|
{
|
||||||
commands.Add(new GuardCommand(printingId));
|
commands.Add(new GuardCommand(printingId));
|
||||||
|
|
@ -2101,6 +2105,9 @@ public sealed class GameEngine
|
||||||
var card = defender.Hand[index];
|
var card = defender.Hand[index];
|
||||||
var def = State.Catalog.CardOf(card.PrintingId);
|
var def = State.Catalog.CardOf(card.PrintingId);
|
||||||
|
|
||||||
|
if (def.Grade > VanguardGrade(defenderIndex))
|
||||||
|
return CommandResult.Reject("Ранг карты защиты выше ранга авангарда.");
|
||||||
|
|
||||||
if (!def.IsSentinel && discardPrintingId is not null)
|
if (!def.IsSentinel && discardPrintingId is not null)
|
||||||
return CommandResult.Reject("Эта карта не требует оплаты стоимости.");
|
return CommandResult.Reject("Эта карта не требует оплаты стоимости.");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -535,10 +535,22 @@ public class GameEngineTests
|
||||||
Assert.False(engine.Execute(new AttackCommand(3, -1, 0)).Accepted);
|
Assert.False(engine.Execute(new AttackCommand(3, -1, 0)).Accepted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Guard_Rejects_Unit_With_Grade_Above_Vanguard()
|
||||||
|
{
|
||||||
|
var engine = ReadyToAttack();
|
||||||
|
SetVanguard(engine, 1, Marron);
|
||||||
|
Give(engine, 1, Gallatin);
|
||||||
|
|
||||||
|
Assert.True(engine.Execute(new AttackCommand(-1, -1)).Accepted);
|
||||||
|
Assert.False(engine.Execute(new GuardCommand(Gallatin)).Accepted);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Perfect_Guard_Nullifies_Attack_When_Cost_Paid()
|
public void Perfect_Guard_Nullifies_Attack_When_Cost_Paid()
|
||||||
{
|
{
|
||||||
var engine = ReadyToAttack();
|
var engine = ReadyToAttack();
|
||||||
|
SetVanguard(engine, 1, Marron);
|
||||||
StackDeck(engine.State.Players[0], Gallatin, Gallatin);
|
StackDeck(engine.State.Players[0], Gallatin, Gallatin);
|
||||||
Give(engine, 1, Iseult);
|
Give(engine, 1, Iseult);
|
||||||
Give(engine, 1, Marron);
|
Give(engine, 1, Marron);
|
||||||
|
|
@ -556,6 +568,7 @@ public class GameEngineTests
|
||||||
public void Perfect_Guard_Without_Cost_Adds_No_Shield()
|
public void Perfect_Guard_Without_Cost_Adds_No_Shield()
|
||||||
{
|
{
|
||||||
var engine = ReadyToAttack();
|
var engine = ReadyToAttack();
|
||||||
|
SetVanguard(engine, 1, Marron);
|
||||||
StackDeck(engine.State.Players[0], Gallatin, Gallatin);
|
StackDeck(engine.State.Players[0], Gallatin, Gallatin);
|
||||||
Give(engine, 1, Iseult);
|
Give(engine, 1, Iseult);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue