diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 52d2d1b4576..f234924d858 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -545,10 +545,11 @@ public class AiController { for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(possibleCounters, player)) { SpellAbility currentSA = sa; + + Player originalActivator = sa.getActivatingPlayer(); // needed for delayed triggers sa.setActivatingPlayer(player); // check everything necessary - - + AiPlayDecision opinion = canPlayAndPayFor(currentSA); //PhaseHandler ph = game.getPhaseHandler(); // System.out.printf("Ai thinks '%s' of %s @ %s %s >>> \n", opinion, sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase()); @@ -559,13 +560,18 @@ public class AiController { } else { // Compare bestSA with this SA final int restrictionLevel = ComputerUtil.counterSpellRestriction(player, currentSA); - + if (restrictionLevel > bestRestriction) { bestRestriction = restrictionLevel; bestSA = currentSA; } } } + + // This was only a simulation, so set the original activator back if there was one + if (originalActivator != null) { + sa.setActivatingPlayer(originalActivator); + } } // TODO - "Look" at Targeted SA and "calculate" the threshold @@ -592,11 +598,17 @@ public class AiController { if (sa.getApi() == ApiType.Counter || sa.getApi() == exceptSA) { continue; } + + Player originalActivator = sa.getActivatingPlayer(); // needed for delayed triggers sa.setActivatingPlayer(player); // TODO: this currently only works as a limited prediction of permanent spells. // Ideally this should cast canPlaySa to determine that the AI is truly able/willing to cast a spell, // but that is currently difficult to implement due to various side effects leading to stack overflow. if (!ComputerUtil.castPermanentInMain1(player, sa) && sa.getHostCard() != null && !sa.getHostCard().isLand() && ComputerUtilCost.canPayCost(sa, player)) { + if (originalActivator != null) { + sa.setActivatingPlayer(originalActivator); // we are only simulating, so set the original activator back + } + if (sa instanceof SpellPermanent) { return sa; } @@ -1118,6 +1130,10 @@ public class AiController { } } + // We need the original activating player in case the SA is on a delayed trigger waiting to be + // activated by another player (e.g. Rainbow Vale EOT trigger). + Player origActivatingPlayer = sa.getActivatingPlayer(); + sa.setActivatingPlayer(player); sa.setLastStateBattlefield(game.getLastStateBattlefield()); sa.setLastStateGraveyard(game.getLastStateGraveyard()); @@ -1125,9 +1141,14 @@ public class AiController { AiPlayDecision opinion = canPlayAndPayFor(sa); // PhaseHandler ph = game.getPhaseHandler(); // System.out.printf("Ai thinks '%s' of %s -> %s @ %s %s >>> \n", opinion, sa.getHostCard(), sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase()); - - if (opinion != AiPlayDecision.WillPlay) + + if (origActivatingPlayer != null) { + sa.setActivatingPlayer(origActivatingPlayer); // since this was only a simulation, restore the original activator + } + + if (opinion != AiPlayDecision.WillPlay) { continue; + } return sa; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java b/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java index c58873d5463..292e47679ca 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java @@ -95,7 +95,6 @@ public class ComputerUtilAbility { public static List getOriginalAndAltCostAbilities(final List originList, final Player player) { final List newAbilities = new ArrayList(); for (SpellAbility sa : originList) { - sa.setActivatingPlayer(player); //add alternative costs as additional spell abilities newAbilities.add(sa); newAbilities.addAll(GameActionUtil.getAlternativeCosts(sa, player)); @@ -103,7 +102,6 @@ public class ComputerUtilAbility { final List result = new ArrayList(); for (SpellAbility sa : newAbilities) { - sa.setActivatingPlayer(player); result.addAll(GameActionUtil.getOptionalCosts(sa)); } return result; diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 01b6d8f5983..e6754587989 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -471,11 +471,19 @@ public class SpecialCardAi { continue; } + Player originalActivator = testSa.getActivatingPlayer(); // needed for delayed triggers testSa.setActivatingPlayer(ai); + + boolean willPlay = false; if (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSa) == AiPlayDecision.WillPlay) { - // the AI is willing to play the spell - return true; + willPlay = true; } + + if (originalActivator != null) { + testSa.setActivatingPlayer(originalActivator); // we are only simulating, so set the original activator back + } + + if (willPlay) { return true; } // The AI seems willing to play the spell } return false; // haven't found anything to play with the excess generated mana diff --git a/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java b/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java index 8e209d24a26..26504cbfb7f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java @@ -156,7 +156,7 @@ public class ManaEffectAi extends SpellAbilityAi { } SpellAbility testSaNoCost = testSa.copyWithNoManaCost(); - testSaNoCost.setActivatingPlayer(ai); + testSaNoCost.setActivatingPlayer(ai); // should be safe since we're operating on a copy if (((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testSaNoCost) == AiPlayDecision.WillPlay) { if (testSa.getHostCard().isPermanent() && !testSa.getHostCard().hasKeyword("Haste") && !ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) { diff --git a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java index 03ad65511b3..a616787a513 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java +++ b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java @@ -81,15 +81,23 @@ public class SpellAbilityPicker { if (sa.isManaAbility()) { continue; } + + Player originalActivator = sa.getActivatingPlayer(); // needed for delayed triggers sa.setActivatingPlayer(player); AiPlayDecision opinion = canPlayAndPayForSim(sa); // print(" " + opinion + ": " + sa); // PhaseHandler ph = game.getPhaseHandler(); // System.out.printf("Ai thinks '%s' of %s -> %s @ %s %s >>> \n", opinion, sa.getHostCard(), sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase()); - - if (opinion != AiPlayDecision.WillPlay) + + if (originalActivator != null) { + sa.setActivatingPlayer(originalActivator); // we are only simulating, so set the original activator back + } + + if (opinion != AiPlayDecision.WillPlay) { continue; + } + candidateSAs.set(writeIndex, sa); writeIndex++; }