diff --git a/server/src/Vanguard.Rules/Engine/GameEngine.cs b/server/src/Vanguard.Rules/Engine/GameEngine.cs
index 69c87d1..a5e1917 100644
--- a/server/src/Vanguard.Rules/Engine/GameEngine.cs
+++ b/server/src/Vanguard.Rules/Engine/GameEngine.cs
@@ -9,10 +9,9 @@ namespace Vanguard.Rules.Engine;
/// Упрощения первого среза (карты со способностями подключаются следующим шагом):
/// * Sentinel добавляет 0 щита и не оплачивает стоимость — Perfect Guard пока не работает;
/// * Boost не реализован;
-/// * триггер на drive check требует выбора целей через ResolveTriggerCommand:
+/// * триггер требует выбора целей через ResolveTriggerCommand:
/// +5000 любой карте в кругах Авангарда или Арьергарда, затем второй эффект;
-/// * триггер на damage check пока применяет только второй эффект,
-/// Stand поднимает первый повёрнутый Арьергард.
+/// правило одинаково для drive check и damage check.
///
public sealed class GameEngine
{
@@ -485,8 +484,12 @@ public sealed class GameEngine
{
State.Step = Step.Drive;
ContinueDriveChecks(battle);
+ return CommandResult.Ok();
}
+ if (pending.Context == TriggerContext.DamageCheck)
+ FinishDamage(pending);
+
return CommandResult.Ok();
}
@@ -532,6 +535,9 @@ public sealed class GameEngine
}
}
+ if (State.PendingTrigger is not null)
+ return;
+
ClearBattle(battle);
}
@@ -567,17 +573,28 @@ public sealed class GameEngine
if (card is null)
return;
+ card.FaceUp = false;
+ State.Players[player].Damage.Add(card);
+ _events.Add(new DamageTakenEvent(player, 1, State.Players[player].Damage.Count));
+
var def = State.Catalog.CardOf(card.PrintingId);
if (def.Trigger != TriggerType.None)
{
_events.Add(new TriggerResolvedEvent(player, card.PrintingId, def.Trigger));
- ApplyDamageTrigger(player, def.Trigger);
+ State.PendingTrigger = new PendingTrigger
+ {
+ Player = player,
+ PrintingId = card.PrintingId,
+ Trigger = def.Trigger,
+ Context = TriggerContext.DamageCheck,
+ GrantsPower = true,
+ RemainingDamage = amount - i - 1,
+ };
+ State.Step = Step.Trigger;
+ State.ActivePlayer = player;
+ return;
}
- card.FaceUp = false;
- State.Players[player].Damage.Add(card);
- _events.Add(new DamageTakenEvent(player, 1, State.Players[player].Damage.Count));
-
if (State.Players[player].Damage.Count >= MaxDamage)
{
Defeat(player);
@@ -586,33 +603,30 @@ public sealed class GameEngine
}
}
- private void ApplyDamageTrigger(int player, TriggerType trigger)
+ /// Продолжает урон после разрешения триггера damage check.
+ private void FinishDamage(PendingTrigger pending)
{
- switch (trigger)
- {
- case TriggerType.Draw:
- Draw(player, 1);
- break;
- case TriggerType.Stand:
- StandFirstRested(player);
- break;
- case TriggerType.Heal:
- Heal(player);
- break;
- }
- }
+ if (State.IsFinished)
+ return;
- private void StandFirstRested(int player)
- {
- var p = State.Players[player];
- for (var i = 0; i < p.RearGuards.Length; i++)
+ var player = pending.Player;
+
+ if (State.Players[player].Damage.Count >= MaxDamage)
{
- if (p.RearGuards[i] is { Rested: true } unit)
- {
- unit.Rested = false;
- return;
- }
+ Defeat(player);
+ return;
}
+
+ if (pending.RemainingDamage > 0)
+ DealDamage(player, pending.RemainingDamage);
+
+ if (State.PendingTrigger is not null)
+ return;
+
+ State.Step = Step.None;
+
+ if (State.Battle is { } battle)
+ ClearBattle(battle);
}
private void Heal(int player)
diff --git a/server/src/Vanguard.Rules/Engine/GameModels.cs b/server/src/Vanguard.Rules/Engine/GameModels.cs
index dcc851a..ec5dde7 100644
--- a/server/src/Vanguard.Rules/Engine/GameModels.cs
+++ b/server/src/Vanguard.Rules/Engine/GameModels.cs
@@ -37,6 +37,9 @@ public sealed class PendingTrigger
/// На drive check триггер сначала даёт +5000 силы, на damage check — нет.
public required bool GrantsPower { get; init; }
+
+ /// Сколько урона осталось раздать после этого триггера (контекст damage check).
+ public int RemainingDamage { get; init; }
}
/// Экземпляр карты в партии. Один принт может встречаться много раз.
diff --git a/server/tests/Vanguard.Rules.Tests/GameEngineTests.cs b/server/tests/Vanguard.Rules.Tests/GameEngineTests.cs
index f7d6f03..fe19eb7 100644
--- a/server/tests/Vanguard.Rules.Tests/GameEngineTests.cs
+++ b/server/tests/Vanguard.Rules.Tests/GameEngineTests.cs
@@ -153,6 +153,30 @@ public class GameEngineTests
Assert.False(rearGuard.Rested);
}
+ [Fact]
+ public void Damage_Trigger_Also_Asks_For_Targets_And_Applies_Effect()
+ {
+ var engine = ReadyToAttack();
+ StackDeck(engine.State.Players[0], Gallatin, Gallatin);
+ StackDeck(engine.State.Players[1], Nike);
+
+ Assert.True(engine.Execute(new AttackCommand(-1, -1)).Accepted);
+ Assert.True(engine.Execute(new PassGuardCommand()).Accepted);
+
+ var pending = engine.State.PendingTrigger;
+ Assert.NotNull(pending);
+ Assert.Equal(TriggerContext.DamageCheck, pending!.Context);
+ Assert.Single(engine.State.Players[1].Damage);
+
+ Assert.True(engine.Execute(new ResolveTriggerCommand(-1, -1)).Accepted);
+
+ Assert.Null(engine.State.PendingTrigger);
+ Assert.Null(engine.State.Battle);
+ Assert.Single(engine.State.Players[1].Damage);
+ Assert.Contains(engine.State.Players[1].Damage, c => c.PrintingId == Nike);
+ Assert.Contains(engine.Events, e => e is TriggerAppliedEvent { Player: 1 });
+ }
+
[Fact]
public void ResolveTrigger_Rejects_When_Nothing_Is_Pending()
{