From 4fb1c866da4b4d27ddfee9797576b5364016c990 Mon Sep 17 00:00:00 2001 From: Agetian Date: Tue, 8 Aug 2017 04:44:05 +0000 Subject: [PATCH] - AI: Avoid infinitely activating AF Untap on another permanent that will then be used to untap the first one (e.g. 2x Kiora's Follower) --- .../main/java/forge/ai/ability/UntapAi.java | 21 +++++++++++++++++++ .../src/main/java/forge/game/cost/Cost.java | 9 ++++++++ 2 files changed, 30 insertions(+) diff --git a/forge-ai/src/main/java/forge/ai/ability/UntapAi.java b/forge-ai/src/main/java/forge/ai/ability/UntapAi.java index 4c38e7462c2..490efa60870 100644 --- a/forge-ai/src/main/java/forge/ai/ability/UntapAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/UntapAi.java @@ -13,6 +13,8 @@ import forge.game.card.CardCollection; import forge.game.card.CardLists; import forge.game.card.CardPredicates.Presets; import forge.game.cost.Cost; +import forge.game.cost.CostTap; +import forge.game.cost.CostTapType; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.player.PlayerCollection; @@ -148,6 +150,25 @@ public class UntapAi extends SpellAbilityAi { final String[] tappablePermanents = {"Creature", "Land", "Artifact"}; untapList = CardLists.getValidCards(untapList, tappablePermanents, source.getController(), source, sa); + // Try to avoid potential infinite recursion, + // e.g. Kiora's Follower untapping another Kiora's Follower and repeating infinitely + if (sa.getPayCosts() != null && sa.getPayCosts().hasOnlySpecificCostType(CostTap.class)) { + CardCollection toRemove = new CardCollection(); + for (Card c : untapList) { + for (SpellAbility ab : c.getAllSpellAbilities()) { + if (ab.getApi() == ApiType.Untap + && ab.getPayCosts() != null + && ab.getPayCosts().hasOnlySpecificCostType(CostTap.class) + && ab.canTarget(source)) { + System.out.println("Found a recursive untap target: " + c); + toRemove.add(c); + break; + } + } + } + untapList.removeAll(toRemove); + } + sa.resetTargets(); while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(sa.getHostCard(), sa)) { Card choice = null; diff --git a/forge-game/src/main/java/forge/game/cost/Cost.java b/forge-game/src/main/java/forge/game/cost/Cost.java index 929b3847c52..25914ac0df4 100644 --- a/forge-game/src/main/java/forge/game/cost/Cost.java +++ b/forge-game/src/main/java/forge/game/cost/Cost.java @@ -73,6 +73,15 @@ public class Cost { return false; } + public final boolean hasOnlySpecificCostType(Class costType) { + for (CostPart p : getCostParts()) { + if (!costType.isInstance(p)) { + return false; + } + } + return true; + } + /** * Gets the cost parts. *