diff --git a/server/src/Vanguard.Rules/Engine/GameEngine.cs b/server/src/Vanguard.Rules/Engine/GameEngine.cs index 0f34134..0a1b9f7 100644 --- a/server/src/Vanguard.Rules/Engine/GameEngine.cs +++ b/server/src/Vanguard.Rules/Engine/GameEngine.cs @@ -12,8 +12,9 @@ namespace Vanguard.Rules.Engine; /// * поддержка работает как отдельная способность: карты 0 и 1 ранга могут передать /// свою силу атакующему Арьергарду из той же колонны, сила действует только в этом бою; /// * перехват — отдельная способность карт 2 ранга: карта из переднего ряда выходит -/// в круг защиты, и сила атаки сравнивается с ней; из заднего ряда — -/// только если эффект разрешил (InterceptFromBackRow); +/// в круг защиты и при атаке по Авангарду, и при атаке по Арьергарду; +/// атакованный Арьергард не может перехватить атаку на самого себя; +/// из заднего ряда — только если эффект разрешил (InterceptFromBackRow); /// * триггер требует выбора целей через ResolveTriggerCommand: /// +5000 любой карте в кругах Авангарда или Арьергарда, затем второй эффект; /// правило одинаково для drive check и damage check; @@ -373,9 +374,6 @@ public sealed class GameEngine if (State.ActivePlayer != defenderIndex) return CommandResult.Reject("Перехватывает только обороняющийся."); - if (battle.TargetSlot < 0) - return CommandResult.Reject("Перехват возможен только при атаке по Арьергарду."); - if (battle.Interceptor is not null) return CommandResult.Reject("Перехват уже объявлен."); @@ -647,16 +645,17 @@ public sealed class GameEngine if (hit) { - if (battle.TargetSlot < 0) + if (battle.Interceptor is not null) + { + // перехватчик уже вышел из круга и уйдёт в сброс при закрытии боя, + // первоначальная цель (Авангард или Арьергард) не страдает + _events.Add(new InterceptorRetiredEvent(defenderIndex, battle.Interceptor.PrintingId)); + } + else if (battle.TargetSlot < 0) { var damage = attackerDef.Critical + attacker.TemporaryCritical; DealDamage(defenderIndex, damage); } - else if (battle.Interceptor is not null) - { - // перехватчик уже вышел из круга и уйдёт в сброс при закрытии боя - _events.Add(new InterceptorRetiredEvent(defenderIndex, battle.Interceptor.PrintingId)); - } else { State.Players[defenderIndex].RearGuards[battle.TargetSlot] = null; diff --git a/server/tests/Vanguard.Rules.Tests/GameEngineTests.cs b/server/tests/Vanguard.Rules.Tests/GameEngineTests.cs index b630eee..6b3d496 100644 --- a/server/tests/Vanguard.Rules.Tests/GameEngineTests.cs +++ b/server/tests/Vanguard.Rules.Tests/GameEngineTests.cs @@ -17,6 +17,7 @@ public class GameEngineTests private const string Govannon = "BT01/045EN"; // Draw private const string Iseult = "BT01/011EN"; // Sentinel, Royal Paladin private const string Nehalem = "BT01/022EN"; // Kagero + private const string Overlord = "BT01/004EN"; // Kagero, 11000 [Fact] public void Start_Places_Starting_Vanguard_And_Draws_Five() @@ -382,6 +383,37 @@ public class GameEngineTests Assert.True(engine.Execute(new InterceptCommand(1)).Accepted); } + [Fact] + public void Intercept_Works_Against_Vanguard_Attack() + { + var engine = StartedEngine(); + engine.Execute(new SkipRideCommand()); + SetVanguard(engine, 0, Overlord); + StackDeck(engine.State.Players[0], Gallatin, Gallatin); + engine.Execute(new EnterBattleCommand()); + + var defender = engine.State.Players[1]; + defender.RearGuards[3] = TakeFromDeck(defender, Gallatin); + + Assert.True(engine.Execute(new AttackCommand(-1, -1)).Accepted); + Assert.True(engine.Execute(new InterceptCommand(3)).Accepted); + Assert.True(engine.Execute(new PassGuardCommand()).Accepted); + + Assert.Empty(defender.Damage); + Assert.NotNull(defender.Vanguard); + Assert.Contains(defender.Drop, c => c.PrintingId == Gallatin); + Assert.Contains(engine.Events, e => e is InterceptorRetiredEvent); + } + + [Fact] + public void Attacked_RearGuard_Cannot_Intercept_Itself() + { + var engine = ReadyToAttackRearGuard(); + + Assert.True(engine.Execute(new AttackCommand(3, 0)).Accepted); + Assert.False(engine.Execute(new InterceptCommand(0)).Accepted); + } + [Fact] public void ResolveTrigger_Rejects_When_Nothing_Is_Pending() { @@ -493,7 +525,7 @@ public class GameEngineTests private List BuildDeck() { - var mustInclude = new[] { Barcgal, Marron, Gallatin, Alfred, Nike, Flogal, Govannon, Iseult, Nehalem }; + var mustInclude = new[] { Barcgal, Marron, Gallatin, Alfred, Nike, Flogal, Govannon, Iseult, Nehalem, Overlord }; var names = mustInclude .Select(_catalog.CardOf) .Select(c => c.NameId)