Skip to content

Commit 5d48a4c

Browse files
Fix AI freeze when declaring attackers with unpayable attack costs (Card-Forge#9998)
1 parent 921b350 commit 5d48a4c

File tree

1 file changed

+20
-0
lines changed

1 file changed

+20
-0
lines changed

forge-ai/src/main/java/forge/ai/AiController.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,10 @@ public void declareAttackers(Player attacker, Combat combat) {
13241324
// Check if we can reinforce with Banding creatures
13251325
aiAtk.reinforceWithBanding(combat);
13261326

1327+
// Per CR 508.1d, the decision to pay attack costs (e.g. Propaganda)
1328+
// is made at declaration time. Remove attackers the AI can't pay for.
1329+
removeUnpayableAttackers(combat);
1330+
13271331
// if invalid: just try an attack declaration that we know to be legal
13281332
if (!CombatUtil.validateAttackers(combat)) {
13291333
combat.clearAttackers();
@@ -1343,6 +1347,22 @@ public void declareAttackers(Player attacker, Combat combat) {
13431347
}
13441348
}
13451349

1350+
private void removeUnpayableAttackers(Combat combat) {
1351+
for (Card attacker : new ArrayList<>(combat.getAttackers())) {
1352+
Cost attackCost = CombatUtil.getAttackCost(game, attacker, combat.getDefenderByAttacker(attacker));
1353+
if (attackCost == null) {
1354+
continue;
1355+
}
1356+
SpellAbility fakeSA = new SpellAbility.EmptySa(attacker, attacker.getController());
1357+
fakeSA.setCardState(attacker.getCurrentState());
1358+
fakeSA.setPayCosts(attackCost);
1359+
fakeSA.setSVar("X", "0");
1360+
if (!ComputerUtilCost.canPayCost(attackCost, fakeSA, player, true)) {
1361+
combat.removeFromCombat(attacker);
1362+
}
1363+
}
1364+
}
1365+
13461366
private List<SpellAbility> singleSpellAbilityList(SpellAbility sa) {
13471367
if (sa == null) {
13481368
return null;

0 commit comments

Comments
 (0)