diff --git a/.gitattributes b/.gitattributes
index 18f38a7fa26..04d6867e7d0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -128,6 +128,7 @@ forge-game/src/main/java/forge/ai/AiAttackController.java svneol=native#text/pla
forge-game/src/main/java/forge/ai/AiBlockController.java svneol=native#text/plain
forge-game/src/main/java/forge/ai/AiController.java svneol=native#text/plain
forge-game/src/main/java/forge/ai/AiCostDecision.java -text
+forge-game/src/main/java/forge/ai/AiPlayDecision.java -text
forge-game/src/main/java/forge/ai/AiProfileUtil.java -text
forge-game/src/main/java/forge/ai/AiProps.java -text
forge-game/src/main/java/forge/ai/ComputerUtil.java svneol=native#text/plain
diff --git a/forge-game/src/main/java/forge/ai/AiController.java b/forge-game/src/main/java/forge/ai/AiController.java
index 839ddee66ee..437ee595553 100644
--- a/forge-game/src/main/java/forge/ai/AiController.java
+++ b/forge-game/src/main/java/forge/ai/AiController.java
@@ -17,39 +17,69 @@
*/
package forge.ai;
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+
import forge.card.CardType;
import forge.card.MagicColor;
+import forge.card.mana.ManaCost;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.game.Game;
import forge.game.GameActionUtil;
import forge.game.GameEntity;
+import forge.game.GlobalRuleChange;
+import forge.game.ability.AbilityFactory;
+import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
-import forge.game.card.*;
+import forge.game.ability.SpellApiBased;
+import forge.game.card.Card;
+import forge.game.card.CardFactoryUtil;
+import forge.game.card.CardLists;
+import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets;
+import forge.game.card.CounterType;
import forge.game.combat.Combat;
+import forge.game.cost.Cost;
import forge.game.cost.CostDiscard;
import forge.game.cost.CostPart;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
-import forge.game.spellability.*;
+import forge.game.replacement.ReplaceMoved;
+import forge.game.replacement.ReplacementEffect;
+import forge.game.spellability.Ability;
+import forge.game.spellability.AbilityManaPart;
+import forge.game.spellability.AbilitySub;
+import forge.game.spellability.OptionalCost;
+import forge.game.spellability.Spell;
+import forge.game.spellability.SpellAbility;
+import forge.game.spellability.SpellPermanent;
+import forge.game.trigger.Trigger;
+import forge.game.trigger.TriggerType;
+import forge.game.trigger.WrappedAbility;
import forge.game.zone.ZoneType;
import forge.item.PaperCard;
import forge.util.Aggregates;
import forge.util.Expressions;
import forge.util.MyRandom;
-import java.util.*;
-import java.util.Map.Entry;
-
/**
*
* ComputerAI_General class.
@@ -137,7 +167,7 @@ public class AiController {
for (final SpellAbility sa : c.getNonManaSpellAbilities()) {
if (sa instanceof SpellPermanent) {
sa.setActivatingPlayer(player);
- if (SpellPermanent.checkETBEffects(c, sa, ApiType.Counter, player)) {
+ if (checkETBEffects(c, sa, ApiType.Counter)) {
spellAbilities.add(sa);
}
}
@@ -146,6 +176,156 @@ public class AiController {
return spellAbilities;
}
+
+ public boolean checkETBEffects(final Card card, final SpellAbility sa, final ApiType api) {
+ boolean rightapi = false;
+
+ if (card.isCreature()
+ && game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noCreatureETBTriggers)) {
+ return api == null;
+ }
+
+ // Trigger play improvements
+ for (final Trigger tr : card.getTriggers()) {
+ // These triggers all care for ETB effects
+
+ final Map params = tr.getMapParams();
+ if (tr.getMode() != TriggerType.ChangesZone) {
+ continue;
+ }
+
+ if (!params.get("Destination").equals(ZoneType.Battlefield.toString())) {
+ continue;
+ }
+
+ if (params.containsKey("ValidCard")) {
+ if (!params.get("ValidCard").contains("Self")) {
+ continue;
+ }
+ if (params.get("ValidCard").contains("notkicked")) {
+ if (sa.isKicked()) {
+ continue;
+ }
+ } else if (params.get("ValidCard").contains("kicked")) {
+ if (params.get("ValidCard").contains("kicked ")) { // want a specific kicker
+ String s = params.get("ValidCard").split("kicked ")[1];
+ if ( "1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
+ if ( "2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
+ } else if (!sa.isKicked()) {
+ continue;
+ }
+ }
+ }
+
+ if (!tr.requirementsCheck(game)) {
+ continue;
+ }
+
+ if (tr.getOverridingAbility() != null) {
+ // Abilities yet
+ continue;
+ }
+
+ // if trigger is not mandatory - no problem
+ if (params.get("OptionalDecider") != null) {
+ continue;
+ }
+
+ // Maybe better considerations
+ final String execute = params.get("Execute");
+ if (execute == null) {
+ continue;
+ }
+ final SpellAbility exSA = AbilityFactory.getAbility(card.getSVar(execute), card);
+
+ if (api != null) {
+ if (exSA.getApi() != api) {
+ continue;
+ } else {
+ rightapi = true;
+ }
+ }
+
+ if (sa != null) {
+ exSA.setActivatingPlayer(sa.getActivatingPlayer());
+ }
+ else {
+ exSA.setActivatingPlayer(player);
+ }
+ exSA.setTrigger(true);
+
+ // Run non-mandatory trigger.
+ // These checks only work if the Executing SpellAbility is an Ability_Sub.
+ if ((exSA instanceof AbilitySub) && !doTrigger(exSA, false)) {
+ // AI would not run this trigger if given the chance
+ return false;
+ }
+ }
+ if (api != null && !rightapi) {
+ return false;
+ }
+
+ // Replacement effects
+ for (final ReplacementEffect re : card.getReplacementEffects()) {
+ // These Replacements all care for ETB effects
+
+ final Map params = re.getMapParams();
+ if (!(re instanceof ReplaceMoved)) {
+ continue;
+ }
+
+ if (!params.get("Destination").equals(ZoneType.Battlefield.toString())) {
+ continue;
+ }
+
+ if (params.containsKey("ValidCard")) {
+ if (!params.get("ValidCard").contains("Self")) {
+ continue;
+ }
+ if (params.get("ValidCard").contains("notkicked")) {
+ if (sa.isKicked()) {
+ continue;
+ }
+ } else if (params.get("ValidCard").contains("kicked")) {
+ if (params.get("ValidCard").contains("kicked ")) { // want a specific kicker
+ String s = params.get("ValidCard").split("kicked ")[1];
+ if ( "1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
+ if ( "2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
+ } else if (!sa.isKicked()) { // otherwise just any must be present
+ continue;
+ }
+ }
+ }
+
+ if (!re.requirementsCheck(game)) {
+ continue;
+ }
+ final SpellAbility exSA = re.getOverridingAbility();
+
+ if (exSA != null) {
+ if (sa != null) {
+ exSA.setActivatingPlayer(sa.getActivatingPlayer());
+ }
+ else {
+ exSA.setActivatingPlayer(player);
+ }
+
+ if (exSA.getActivatingPlayer() == null) {
+ throw new InvalidParameterException("Executing SpellAbility for Replacement Effect has no activating player");
+ }
+ }
+
+ // ETBReplacement uses overriding abilities.
+ // These checks only work if the Executing SpellAbility is an Ability_Sub.
+ if (exSA != null && (exSA instanceof AbilitySub) && !doTrigger(exSA, false)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
private List getOriginalAndAltCostAbilities(final List originList)
{
final ArrayList newAbilities = new ArrayList();
@@ -266,36 +446,7 @@ public class AiController {
landList = CardLists.filter(landList, new Predicate() {
@Override
public boolean apply(final Card c) {
- if (c.getSVar("NeedsToPlay").length() > 0) {
- final String needsToPlay = c.getSVar("NeedsToPlay");
- List list = game.getCardsIn(ZoneType.Battlefield);
-
- list = CardLists.getValidCards(list, needsToPlay.split(","), c.getController(), c);
- if (list.isEmpty()) {
- return false;
- }
- }
- if (c.getSVar("NeedsToPlayVar").length() > 0) {
- final String needsToPlay = c.getSVar("NeedsToPlayVar");
- int x = 0;
- int y = 0;
- String sVar = needsToPlay.split(" ")[0];
- String comparator = needsToPlay.split(" ")[1];
- String compareTo = comparator.substring(2);
- try {
- x = Integer.parseInt(sVar);
- } catch (final NumberFormatException e) {
- x = CardFactoryUtil.xCount(c, c.getSVar(sVar));
- }
- try {
- y = Integer.parseInt(compareTo);
- } catch (final NumberFormatException e) {
- y = CardFactoryUtil.xCount(c, c.getSVar(compareTo));
- }
- if (!Expressions.compare(x, comparator, y)) {
- return false;
- }
- }
+ canPlaySpellBasic(c);
if (c.isType("Legendary") && !c.getName().equals("Flagstones of Trokair")) {
final List list = player.getCardsIn(ZoneType.Battlefield);
if (Iterables.any(list, CardPredicates.nameEquals(c.getName()))) {
@@ -420,7 +571,12 @@ public class AiController {
SpellAbility currentSA = sa;
sa.setActivatingPlayer(player);
// check everything necessary
- if (canPlayAndPayFor(currentSA)) {
+
+
+ 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());
+ if (opinion == AiPlayDecision.WillPlay) {
if (bestSA == null) {
bestSA = currentSA;
bestRestriction = ComputerUtil.counterSpellRestriction(player, currentSA);
@@ -441,51 +597,108 @@ public class AiController {
return bestSA;
} // playCounterSpell()
- // if return true, go to next phase
- /**
- *
- * playSpellAbilities.
- *
- *
- * @param all
- * an array of {@link forge.game.spellability.SpellAbility}
- * objects.
- * @return a boolean.
- */
- private SpellAbility chooseSpellAbilyToPlay(final List all, boolean skipCounter) {
- if ( all == null || all.isEmpty() )
- return null;
-
- Collections.sort(all, saComparator); // put best spells first
-
- for (final SpellAbility sa : getOriginalAndAltCostAbilities(all)) {
- // Don't add Counterspells to the "normal" playcard lookups
- if (sa.getApi() == ApiType.Counter && skipCounter) {
- continue;
- }
- sa.setActivatingPlayer(player);
-
- if (!canPlayAndPayFor(sa))
- continue;
-
- return sa;
- }
-
- return null;
- } // playCards()
-
-
// This is for playing spells regularly (no Cascade/Ripple etc.)
- private boolean canPlayAndPayFor(final SpellAbility sa) {
+ private AiPlayDecision canPlayAndPayFor(final SpellAbility sa) {
if (!sa.canPlay()) {
- return false;
+ return AiPlayDecision.CantPlaySa;
}
- //System.out.printf("Ai thinks of %s @ %s >>> ", sa, sa.getActivatingPlayer().getGame().getPhaseHandler().debugPrintState());
- if (!sa.canPlayAI(player)) {
- return false;
+
+ AiPlayDecision op = canPlaySa(sa);
+ if (op != AiPlayDecision.WillPlay) {
+ return op;
}
- //System.out.printf("wouldPlay: %s, canPay: %s%n", aiWouldPlay, canPay);
- return ComputerUtilCost.canPayCost(sa, player);
+ return ComputerUtilCost.canPayCost(sa, player) ? AiPlayDecision.WillPlay : AiPlayDecision.CantAfford;
+ }
+
+ public AiPlayDecision canPlaySa(SpellAbility sa) {
+ final Card card = sa.getHostCard();
+ if ( sa instanceof WrappedAbility ) {
+ return canPlaySa(((WrappedAbility) sa).getWrappedAbility());
+ }
+ if( sa.getApi() != null ) {
+ boolean canPlay = sa.getApi().getAi().canPlayAIWithSubs(player, sa);
+ if(!canPlay)
+ return AiPlayDecision.CantPlayAi;
+ }
+ if( sa instanceof SpellPermanent ) {
+ ManaCost mana = sa.getPayCosts().getTotalMana();
+ if (mana.countX() > 0) {
+ // Set PayX here to maximum value.
+ final int xPay = ComputerUtilMana.determineLeftoverMana(sa, player);
+ if (xPay <= 0) {
+ return AiPlayDecision.CantAffordX;
+ }
+ card.setSVar("PayX", Integer.toString(xPay));
+ }
+ // Prevent the computer from summoning Ball Lightning type creatures after attacking
+ if (card.hasKeyword("At the beginning of the end step, sacrifice CARDNAME.")
+ && (game.getPhaseHandler().isPlayerTurn(player.getOpponent())
+ || game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS))) {
+ return AiPlayDecision.AnotherTime;
+ }
+
+ // Prevent the computer from summoning Ball Lightning type creatures after attacking
+ if (card.hasStartOfKeyword("You may cast CARDNAME as though it had flash. If") && !card.getController().couldCastSorcery(sa)) {
+ return AiPlayDecision.AnotherTime;
+ }
+
+ // Wait for Main2 if possible
+ if (game.getPhaseHandler().is(PhaseType.MAIN1)
+ && game.getPhaseHandler().isPlayerTurn(player)
+ && player.getManaPool().totalMana() <= 0
+ && !ComputerUtil.castPermanentInMain1(player, sa)) {
+ return AiPlayDecision.WaitForMain2;
+ }
+ // save cards with flash for surprise blocking
+ if (card.hasKeyword("Flash")
+ && (player.isUnlimitedHandSize() || player.getCardsIn(ZoneType.Hand).size() <= player.getMaxHandSize())
+ && player.getManaPool().totalMana() <= 0
+ && (game.getPhaseHandler().isPlayerTurn(player)
+ || game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
+ && !card.hasETBTrigger())
+ && !ComputerUtil.castPermanentInMain1(player, sa)) {
+ return AiPlayDecision.AnotherTime;
+ }
+
+ return canPlayFromEffectAI((SpellPermanent)sa, false, true);
+ } else if( sa instanceof Spell ) {
+ return canPlaySpellBasic(card);
+ }
+ return AiPlayDecision.WillPlay;
+ }
+
+ private AiPlayDecision canPlaySpellBasic(final Card card) {
+ if (card.getSVar("NeedsToPlay").length() > 0) {
+ final String needsToPlay = card.getSVar("NeedsToPlay");
+ List list = game.getCardsIn(ZoneType.Battlefield);
+
+ list = CardLists.getValidCards(list, needsToPlay.split(","), card.getController(), card);
+ if (list.isEmpty()) {
+ return AiPlayDecision.MissingNeededCards;
+ }
+ }
+ if (card.getSVar("NeedsToPlayVar").length() > 0) {
+ final String needsToPlay = card.getSVar("NeedsToPlayVar");
+ int x = 0;
+ int y = 0;
+ String sVar = needsToPlay.split(" ")[0];
+ String comparator = needsToPlay.split(" ")[1];
+ String compareTo = comparator.substring(2);
+ try {
+ x = Integer.parseInt(sVar);
+ } catch (final NumberFormatException e) {
+ x = CardFactoryUtil.xCount(card, card.getSVar(sVar));
+ }
+ try {
+ y = Integer.parseInt(compareTo);
+ } catch (final NumberFormatException e) {
+ y = CardFactoryUtil.xCount(card, card.getSVar(compareTo));
+ }
+ if (!Expressions.compare(x, comparator, y)) {
+ return AiPlayDecision.NeedsToPlayCriteriaNotMet;
+ }
+ }
+ return AiPlayDecision.WillPlay;
}
// not sure "playing biggest spell" matters?
@@ -700,11 +913,11 @@ public class AiController {
sa.setActivatingPlayer(player);
//Spells
if (sa instanceof Spell) {
- if (!((Spell) sa).canPlayFromEffectAI(player, mandatory, withoutPayingManaCost)) {
+ if (AiPlayDecision.WillPlay != canPlayFromEffectAI((Spell) sa, mandatory, withoutPayingManaCost)) {
continue;
}
} else {
- if (sa.canPlayAI(player)) {
+ if (AiPlayDecision.WillPlay == canPlaySa(sa)) {
continue;
}
}
@@ -720,6 +933,91 @@ public class AiController {
return null;
}
+ public AiPlayDecision canPlayFromEffectAI(Spell spell, boolean mandatory, boolean withoutPayingManaCost) {
+
+ final Card card = spell.getHostCard();
+ if( spell instanceof SpellApiBased )
+ {
+ boolean chance = false;
+ if (withoutPayingManaCost) {
+ chance = spell.getApi().getAi().doTriggerNoCostWithSubs(player, spell, mandatory);
+ } else {
+ chance = spell.getApi().getAi().doTriggerAI(player, spell, mandatory);
+ }
+ if (!chance)
+ return AiPlayDecision.TargetingFailed;
+ return canPlaySa(spell);
+ }
+
+ if ( spell instanceof SpellPermanent) {
+ if (mandatory) {
+ return AiPlayDecision.WillPlay;
+ }
+ ManaCost mana = spell.getPayCosts().getTotalMana();
+ final Cost cost = spell.getPayCosts();
+
+ if (cost != null) {
+ // AI currently disabled for these costs
+ if (!ComputerUtilCost.checkLifeCost(player, cost, card, 4, null)) {
+ return AiPlayDecision.CostNotAcceptable;
+ }
+
+ if (!ComputerUtilCost.checkDiscardCost(player, cost, card)) {
+ return AiPlayDecision.CostNotAcceptable;
+ }
+
+ if (!ComputerUtilCost.checkSacrificeCost(player, cost, card)) {
+ return AiPlayDecision.CostNotAcceptable;
+ }
+
+ if (!ComputerUtilCost.checkRemoveCounterCost(cost, card)) {
+ return AiPlayDecision.CostNotAcceptable;
+ }
+ }
+
+ // check on legendary
+ if (card.isType("Legendary")
+ && !game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule)) {
+ final List list = player.getCardsIn(ZoneType.Battlefield);
+ if (Iterables.any(list, CardPredicates.nameEquals(card.getName()))) {
+ return AiPlayDecision.WouldDestroyLegend;
+ }
+ }
+ if (card.isPlaneswalker()) {
+ List list = player.getCardsIn(ZoneType.Battlefield);
+ list = CardLists.filter(list, CardPredicates.Presets.PLANEWALKERS);
+
+ List type = card.getType();
+ final String subtype = type.get(type.size() - 1);
+ final List cl = CardLists.getType(list, subtype);
+ if (!cl.isEmpty()) {
+ return AiPlayDecision.WouldDestroyOtherPlaneswalker;
+ }
+ }
+ if (card.isType("World")) {
+ List list = player.getCardsIn(ZoneType.Battlefield);
+ list = CardLists.getType(list, "World");
+ if (!list.isEmpty()) {
+ return AiPlayDecision.WouldDestroyWorldEnchantment;
+ }
+ }
+
+ if (card.isCreature() && (card.getNetDefense() <= 0) && !card.hasStartOfKeyword("etbCounter")
+ && mana.countX() == 0 && !card.hasETBTrigger()
+ && !card.hasETBReplacement()) {
+ return AiPlayDecision.WouldBecomeZeroToughnessCreature;
+ }
+
+ if (!checkETBEffects(card, spell, null)) {
+ return AiPlayDecision.BadEtbEffects;
+ }
+ if (ComputerUtil.damageFromETB(player, card) >= player.getLife() && player.canLoseLife()) {
+ return AiPlayDecision.BadEtbEffects;
+ }
+ }
+ return canPlaySpellBasic(card);
+ }
+
public SpellAbility choooseSpellAbilityToPlay() {
final PhaseType phase = game.getPhaseHandler().getPhase();
@@ -784,8 +1082,37 @@ public class AiController {
return counterETB;
}
- return chooseSpellAbilyToPlay(getSpellAbilities(cards), true);
+ SpellAbility result = chooseSpellAbilyToPlay(getSpellAbilities(cards), true);
+ if( null == result)
+ return null;
+ return result;
}
+
+ private SpellAbility chooseSpellAbilyToPlay(final List all, boolean skipCounter) {
+ if ( all == null || all.isEmpty() )
+ return null;
+
+ Collections.sort(all, saComparator); // put best spells first
+
+ for (final SpellAbility sa : getOriginalAndAltCostAbilities(all)) {
+ // Don't add Counterspells to the "normal" playcard lookups
+ if (sa.getApi() == ApiType.Counter && skipCounter) {
+ continue;
+ }
+ sa.setActivatingPlayer(player);
+
+ 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)
+ continue;
+
+ return sa;
+ }
+
+ return null;
+ }
public List chooseCardsToDelve(int colorlessCost, List grave) {
List toExile = new ArrayList();
@@ -818,13 +1145,74 @@ public class AiController {
return toExile;
}
+ public boolean doTrigger(SpellAbility spell, boolean mandatory) {
+
+ if ( spell.getApi() != null )
+ return spell.getApi().getAi().doTriggerAI(player, spell, mandatory);
+ if ( spell instanceof WrappedAbility )
+ return doTrigger(((WrappedAbility)spell).getWrappedAbility(), mandatory);
+
+ return false;
+ }
+
+ /**
+ * Ai should run.
+ *
+ * @param sa the sa
+ * @param ai
+ * @return true, if successful
+ */
+ public final boolean aiShouldRun(final ReplacementEffect effect, final SpellAbility sa) {
+ Card hostCard = effect.getHostCard();
+ if (effect.getMapParams().containsKey("AICheckSVar")) {
+ System.out.println("aiShouldRun?" + sa);
+ final String svarToCheck = effect.getMapParams().get("AICheckSVar");
+ String comparator = "GE";
+ int compareTo = 1;
+
+ if (effect.getMapParams().containsKey("AISVarCompare")) {
+ final String fullCmp = effect.getMapParams().get("AISVarCompare");
+ comparator = fullCmp.substring(0, 2);
+ final String strCmpTo = fullCmp.substring(2);
+ try {
+ compareTo = Integer.parseInt(strCmpTo);
+ } catch (final Exception ignored) {
+ if (sa == null) {
+ compareTo = CardFactoryUtil.xCount(hostCard, hostCard.getSVar(strCmpTo));
+ } else {
+ compareTo = AbilityUtils.calculateAmount(hostCard, hostCard.getSVar(strCmpTo), sa);
+ }
+ }
+ }
+
+ int left = 0;
+
+ if (sa == null) {
+ left = CardFactoryUtil.xCount(hostCard, hostCard.getSVar(svarToCheck));
+ } else {
+ left = AbilityUtils.calculateAmount(hostCard, svarToCheck, sa);
+ }
+ System.out.println("aiShouldRun?" + left + comparator + compareTo);
+ if (Expressions.compare(left, comparator, compareTo)) {
+ return true;
+ }
+ } else if (effect.getMapParams().containsKey("AICheckDredge")) {
+ return player.getCardsIn(ZoneType.Library).size() > 8 || player.isCardInPlay("Laboratory Maniac");
+ } else if (sa != null && doTrigger(sa, false)) {
+ return true;
+ }
+
+ return false;
+ }
+
+
public List chooseSaToActivateFromOpeningHand(List usableFromOpeningHand) {
// AI would play everything. But limits to one copy of (Leyline of Singularity) and (Gemstone Caverns)
List result = new ArrayList();
for(SpellAbility sa : usableFromOpeningHand) {
// Is there a better way for the AI to decide this?
- if (sa.doTrigger(false, player)) {
+ if (doTrigger(sa, false)) {
result.add(sa);
}
}
diff --git a/forge-game/src/main/java/forge/ai/AiPlayDecision.java b/forge-game/src/main/java/forge/ai/AiPlayDecision.java
new file mode 100644
index 00000000000..e92a85433a1
--- /dev/null
+++ b/forge-game/src/main/java/forge/ai/AiPlayDecision.java
@@ -0,0 +1,20 @@
+package forge.ai;
+
+public enum AiPlayDecision {
+ WillPlay,
+ CantPlaySa,
+ CantPlayAi,
+ CantAfford,
+ CantAffordX,
+ WaitForMain2,
+ AnotherTime,
+ MissingNeededCards,
+ NeedsToPlayCriteriaNotMet,
+ TargetingFailed,
+ CostNotAcceptable,
+ WouldDestroyLegend,
+ WouldDestroyOtherPlaneswalker,
+ WouldBecomeZeroToughnessCreature,
+ WouldDestroyWorldEnchantment,
+ BadEtbEffects;
+}
\ No newline at end of file
diff --git a/forge-game/src/main/java/forge/ai/ComputerUtilMana.java b/forge-game/src/main/java/forge/ai/ComputerUtilMana.java
index ba5c0040686..33387f5bb89 100644
--- a/forge-game/src/main/java/forge/ai/ComputerUtilMana.java
+++ b/forge-game/src/main/java/forge/ai/ComputerUtilMana.java
@@ -630,7 +630,7 @@ public class ComputerUtilMana {
// don't use abilities with dangerous drawbacks
AbilitySub sub = m.getSubAbility();
if (sub != null && !card.getName().equals("Pristine Talisman") && !card.getName().equals("Zhur-Taa Druid")) {
- if (!sub.getAi().chkDrawbackWithSubs(ai, sub)) {
+ if (!sub.getApi().getAi().chkDrawbackWithSubs(ai, sub)) {
continue;
}
needsLimitedResources = true; // TODO: check for good drawbacks (gainLife)
@@ -688,7 +688,7 @@ public class ComputerUtilMana {
// don't use abilities with dangerous drawbacks
AbilitySub sub = m.getSubAbility();
if (sub != null) {
- if (!sub.getAi().chkDrawbackWithSubs(ai, sub)) {
+ if (!sub.getApi().getAi().chkDrawbackWithSubs(ai, sub)) {
continue;
}
}
diff --git a/forge-game/src/main/java/forge/ai/SpellAbilityAi.java b/forge-game/src/main/java/forge/ai/SpellAbilityAi.java
index 612d23e9281..d6ea74a3c30 100644
--- a/forge-game/src/main/java/forge/ai/SpellAbilityAi.java
+++ b/forge-game/src/main/java/forge/ai/SpellAbilityAi.java
@@ -136,7 +136,7 @@ public abstract class SpellAbilityAi extends SaTargetRoutines {
*/
public boolean chkDrawbackWithSubs(Player aiPlayer, AbilitySub ab) {
final AbilitySub subAb = ab.getSubAbility();
- return ab.getAi().chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb));
+ return ab.getApi().getAi().chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb));
}
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
diff --git a/forge-game/src/main/java/forge/ai/ability/ChangeZoneAi.java b/forge-game/src/main/java/forge/ai/ability/ChangeZoneAi.java
index 246b24f9ecb..de08a4d8a38 100644
--- a/forge-game/src/main/java/forge/ai/ability/ChangeZoneAi.java
+++ b/forge-game/src/main/java/forge/ai/ability/ChangeZoneAi.java
@@ -307,7 +307,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
final AbilitySub subAb = sa.getSubAbility();
- return subAb == null || subAb.getAi().chkDrawbackWithSubs(ai, subAb);
+ return subAb == null || subAb.getApi().getAi().chkDrawbackWithSubs(ai, subAb);
}
/**
@@ -645,7 +645,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
final AbilitySub subAb = sa.getSubAbility();
- chance &= subAb == null || subAb.getAi().chkDrawbackWithSubs(ai, subAb);
+ chance &= subAb == null || subAb.getApi().getAi().chkDrawbackWithSubs(ai, subAb);
return chance;
}
diff --git a/forge-game/src/main/java/forge/ai/ability/CharmAi.java b/forge-game/src/main/java/forge/ai/ability/CharmAi.java
index a1d5b3515eb..5f0c279bfc4 100644
--- a/forge-game/src/main/java/forge/ai/ability/CharmAi.java
+++ b/forge-game/src/main/java/forge/ai/ability/CharmAi.java
@@ -1,8 +1,11 @@
package forge.ai.ability;
+import forge.ai.AiController;
+import forge.ai.AiPlayDecision;
import forge.ai.SpellAbilityAi;
import forge.game.ability.effects.CharmEffect;
import forge.game.player.Player;
+import forge.game.player.PlayerControllerAi;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.util.Aggregates;
@@ -45,18 +48,18 @@ public class CharmAi extends SpellAbilityAi {
return choices.subList(1, choices.size());
}
-
+ AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
for (int i = 0; i < num; i++) {
AbilitySub thisPick = null;
for (SpellAbility sub : choices) {
sub.setActivatingPlayer(ai);
- if (!playNow && sub.canPlayAI(ai)) {
+ if (!playNow && AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
thisPick = (AbilitySub) sub;
choices.remove(sub);
playNow = true;
break;
}
- if ((playNow || i < num - 1) && sub.doTrigger(false, ai)) {
+ if ((playNow || i < num - 1) && aic.doTrigger(sub, false)) {
thisPick = (AbilitySub) sub;
choices.remove(sub);
break;
@@ -71,7 +74,7 @@ public class CharmAi extends SpellAbilityAi {
AbilitySub thisPick = null;
for (SpellAbility sub : choices) {
sub.setActivatingPlayer(ai);
- if (sub.doTrigger(true, ai)) {
+ if (aic.doTrigger(sub, true)) {
thisPick = (AbilitySub) sub;
choices.remove(sub);
break;
diff --git a/forge-game/src/main/java/forge/ai/ability/DelayedTriggerAi.java b/forge-game/src/main/java/forge/ai/ability/DelayedTriggerAi.java
index 9f3a2acc263..57bade19552 100644
--- a/forge-game/src/main/java/forge/ai/ability/DelayedTriggerAi.java
+++ b/forge-game/src/main/java/forge/ai/ability/DelayedTriggerAi.java
@@ -1,8 +1,11 @@
package forge.ai.ability;
+import forge.ai.AiController;
+import forge.ai.AiPlayDecision;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityFactory;
import forge.game.player.Player;
+import forge.game.player.PlayerControllerAi;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
@@ -15,9 +18,9 @@ public class DelayedTriggerAi extends SpellAbilityAi {
trigsa.setActivatingPlayer(ai);
if (trigsa instanceof AbilitySub) {
- return ((AbilitySub) trigsa).getAi().chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
+ return ((AbilitySub) trigsa).getApi().getAi().chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
} else {
- return trigsa.canPlayAI(ai);
+ return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
}
}
@@ -25,12 +28,13 @@ public class DelayedTriggerAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final String svarName = sa.getParam("Execute");
final SpellAbility trigsa = AbilityFactory.getAbility(sa.getHostCard().getSVar(svarName), sa.getHostCard());
+ AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
trigsa.setActivatingPlayer(ai);
if (!sa.hasParam("OptionalDecider")) {
- return trigsa.doTrigger(true, ai);
+ return aic.doTrigger(trigsa, true);
} else {
- return trigsa.doTrigger(!sa.getParam("OptionalDecider").equals("You"), ai);
+ return aic.doTrigger(trigsa, !sa.getParam("OptionalDecider").equals("You"));
}
}
@@ -39,7 +43,7 @@ public class DelayedTriggerAi extends SpellAbilityAi {
final String svarName = sa.getParam("Execute");
final SpellAbility trigsa = AbilityFactory.getAbility(sa.getHostCard().getSVar(svarName), sa.getHostCard());
trigsa.setActivatingPlayer(ai);
- return trigsa.canPlayAI(ai);
+ return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
}
}
diff --git a/forge-game/src/main/java/forge/ai/ability/PeekAndRevealAi.java b/forge-game/src/main/java/forge/ai/ability/PeekAndRevealAi.java
index 1a7ed001815..1fe0c9b5f72 100644
--- a/forge-game/src/main/java/forge/ai/ability/PeekAndRevealAi.java
+++ b/forge-game/src/main/java/forge/ai/ability/PeekAndRevealAi.java
@@ -32,7 +32,7 @@ public class PeekAndRevealAi extends SpellAbilityAi {
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
AbilitySub subAb = sa.getSubAbility();
- return subAb != null && subAb.getAi().chkDrawbackWithSubs(player, subAb);
+ return subAb != null && subAb.getApi().getAi().chkDrawbackWithSubs(player, subAb);
}
}
diff --git a/forge-game/src/main/java/forge/ai/ability/PlayAi.java b/forge-game/src/main/java/forge/ai/ability/PlayAi.java
index ca4f8a64673..4cfe85f1659 100644
--- a/forge-game/src/main/java/forge/ai/ability/PlayAi.java
+++ b/forge-game/src/main/java/forge/ai/ability/PlayAi.java
@@ -1,6 +1,8 @@
package forge.ai.ability;
import com.google.common.base.Predicate;
+
+import forge.ai.AiPlayDecision;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi;
@@ -10,6 +12,7 @@ import forge.game.card.CardLists;
import forge.game.cost.Cost;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
+import forge.game.player.PlayerControllerAi;
import forge.game.spellability.Spell;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
@@ -120,7 +123,9 @@ public class PlayAi extends SpellAbilityAi {
Spell spell = (Spell) s;
s.setActivatingPlayer(ai);
// timing restrictions still apply
- if (s.getRestrictions().checkTimingRestrictions(c, s) && spell.canPlayFromEffectAI(ai, false, true)) {
+ if (!s.getRestrictions().checkTimingRestrictions(c, s))
+ continue;
+ if( AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, false, true)) {
return true;
}
}
diff --git a/forge-game/src/main/java/forge/ai/ability/RepeatAi.java b/forge-game/src/main/java/forge/ai/ability/RepeatAi.java
index 3d5069cb8d6..4b5840630bd 100644
--- a/forge-game/src/main/java/forge/ai/ability/RepeatAi.java
+++ b/forge-game/src/main/java/forge/ai/ability/RepeatAi.java
@@ -1,10 +1,12 @@
package forge.ai.ability;
+import forge.ai.AiController;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityFactory;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
+import forge.game.player.PlayerControllerAi;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
@@ -54,6 +56,7 @@ public class RepeatAi extends SpellAbilityAi {
repeat.setActivatingPlayer(sa.getActivatingPlayer());
((AbilitySub) repeat).setParent(sa);
- return repeat.doTrigger(mandatory, ai);
+ AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
+ return aic.doTrigger(repeat, mandatory);
}
}
diff --git a/forge-game/src/main/java/forge/game/ability/AbilityApiBased.java b/forge-game/src/main/java/forge/game/ability/AbilityApiBased.java
index f81d91785d2..87f2a443815 100644
--- a/forge-game/src/main/java/forge/game/ability/AbilityApiBased.java
+++ b/forge-game/src/main/java/forge/game/ability/AbilityApiBased.java
@@ -1,6 +1,5 @@
package forge.game.ability;
-import forge.ai.SpellAbilityAi;
import forge.game.ability.effects.ChangeZoneAllEffect;
import forge.game.ability.effects.ChangeZoneEffect;
import forge.game.ability.effects.ManaEffect;
@@ -8,7 +7,6 @@ import forge.game.ability.effects.ManaReflectedEffect;
import forge.game.card.Card;
import forge.game.card.CardFactory;
import forge.game.cost.Cost;
-import forge.game.player.Player;
import forge.game.spellability.AbilityActivated;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.TargetRestrictions;
@@ -17,7 +15,6 @@ import java.util.Map;
public class AbilityApiBased extends AbilityActivated {
private final SpellAbilityEffect effect;
- private final SpellAbilityAi ai;
private static final long serialVersionUID = -4183793555528531978L;
@@ -26,7 +23,6 @@ public class AbilityApiBased extends AbilityActivated {
mapParams.putAll(params0);
api = api0;
effect = api.getSpellEffect();
- ai = api.getAi();
if (effect instanceof ManaEffect || effect instanceof ManaReflectedEffect) {
@@ -62,14 +58,4 @@ public class AbilityApiBased extends AbilityActivated {
public void resolve() {
effect.resolve(this);
}
-
- @Override
- public boolean canPlayAI(Player aiPlayer) {
- return ai.canPlayAIWithSubs(aiPlayer, this);
- }
-
- @Override
- public boolean doTrigger(final boolean mandatory, Player aiPlayer) {
- return ai.doTriggerAI(aiPlayer, this, mandatory);
- }
}
diff --git a/forge-game/src/main/java/forge/game/ability/SpellApiBased.java b/forge-game/src/main/java/forge/game/ability/SpellApiBased.java
index 315ee1a71a5..ba4c3020548 100644
--- a/forge-game/src/main/java/forge/game/ability/SpellApiBased.java
+++ b/forge-game/src/main/java/forge/game/ability/SpellApiBased.java
@@ -1,13 +1,11 @@
package forge.game.ability;
-import forge.ai.SpellAbilityAi;
import forge.game.ability.effects.ChangeZoneAllEffect;
import forge.game.ability.effects.ChangeZoneEffect;
import forge.game.ability.effects.ManaEffect;
import forge.game.ability.effects.ManaReflectedEffect;
import forge.game.card.Card;
import forge.game.cost.Cost;
-import forge.game.player.Player;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.Spell;
import forge.game.spellability.TargetRestrictions;
@@ -17,8 +15,7 @@ import java.util.Map;
public class SpellApiBased extends Spell {
private static final long serialVersionUID = -6741797239508483250L;
private final SpellAbilityEffect effect;
- private final SpellAbilityAi ai;
-
+
public SpellApiBased(ApiType api0, Card sourceCard, Cost abCost, TargetRestrictions tgt, Map params0) {
super(sourceCard, abCost);
this.setTargetRestrictions(tgt);
@@ -26,7 +23,6 @@ public class SpellApiBased extends Spell {
mapParams.putAll(params0);
api = api0;
effect = api.getSpellEffect();
- ai = api.getAi();
if (effect instanceof ManaEffect || effect instanceof ManaReflectedEffect) {
this.setManaPart(new AbilityManaPart(sourceCard, mapParams));
@@ -46,24 +42,8 @@ public class SpellApiBased extends Spell {
* @see forge.card.spellability.SpellAbility#resolve()
*/
- @Override
- public boolean canPlayAI(Player aiPlayer) {
- return ai.canPlayAIWithSubs(aiPlayer, this) && super.canPlayAI(aiPlayer);
- }
-
@Override
public void resolve() {
effect.resolve(this);
}
-
- @Override
- public boolean canPlayFromEffectAI(Player aiPlayer, final boolean mandatory, final boolean withOutManaCost) {
- boolean chance = false;
- if (withOutManaCost) {
- chance = ai.doTriggerNoCostWithSubs(aiPlayer, this, mandatory);
- } else {
- chance = ai.doTriggerAI(aiPlayer, this, mandatory);
- }
- return chance && super.canPlayAI(aiPlayer);
- }
}
diff --git a/forge-game/src/main/java/forge/game/ability/StaticAbilityApiBased.java b/forge-game/src/main/java/forge/game/ability/StaticAbilityApiBased.java
index 87c6c91ef0b..d85057207d8 100644
--- a/forge-game/src/main/java/forge/game/ability/StaticAbilityApiBased.java
+++ b/forge-game/src/main/java/forge/game/ability/StaticAbilityApiBased.java
@@ -1,11 +1,9 @@
package forge.game.ability;
-import forge.ai.SpellAbilityAi;
import forge.game.ability.effects.ChangeZoneAllEffect;
import forge.game.ability.effects.ChangeZoneEffect;
import forge.game.card.Card;
import forge.game.cost.Cost;
-import forge.game.player.Player;
import forge.game.spellability.AbilityStatic;
import forge.game.spellability.TargetRestrictions;
@@ -14,14 +12,12 @@ import java.util.Map;
public class StaticAbilityApiBased extends AbilityStatic {
private final SpellAbilityEffect effect;
- private final SpellAbilityAi ai;
public StaticAbilityApiBased(ApiType api0, Card sourceCard, Cost abCost, TargetRestrictions tgt, Map params0) {
super(sourceCard, abCost, tgt);
mapParams.putAll(params0);
api = api0;
effect = api.getSpellEffect();
- ai = api.getAi();
if (effect instanceof ChangeZoneEffect || effect instanceof ChangeZoneAllEffect) {
AbilityFactory.adjustChangeZoneTarget(mapParams, this);
@@ -41,14 +37,4 @@ public class StaticAbilityApiBased extends AbilityStatic {
public void resolve() {
effect.resolve(this);
}
-
- @Override
- public boolean canPlayAI(Player aiPlayer) {
- return ai.canPlayAIWithSubs(aiPlayer, this);
- }
-
- @Override
- public boolean doTrigger(final boolean mandatory, Player aiPlayer) {
- return ai.doTriggerAI(aiPlayer, this, mandatory);
- }
}
diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java
index af42b68374d..0f62f5f7846 100644
--- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java
+++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java
@@ -20,8 +20,6 @@ package forge.game.card;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import forge.Command;
-import forge.ai.ComputerUtil;
-import forge.ai.ComputerUtilCost;
import forge.card.CardCharacteristicName;
import forge.card.CardType;
import forge.card.ColorSet;
@@ -39,12 +37,19 @@ import forge.game.card.CardPredicates.Presets;
import forge.game.cost.Cost;
import forge.game.event.GameEventCardStatsChanged;
import forge.game.phase.PhaseHandler;
-import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementHandler;
import forge.game.replacement.ReplacementLayer;
-import forge.game.spellability.*;
+import forge.game.spellability.Ability;
+import forge.game.spellability.AbilityActivated;
+import forge.game.spellability.AbilityStatic;
+import forge.game.spellability.AbilitySub;
+import forge.game.spellability.OptionalCost;
+import forge.game.spellability.Spell;
+import forge.game.spellability.SpellAbility;
+import forge.game.spellability.SpellAbilityRestriction;
+import forge.game.spellability.TargetRestrictions;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.game.zone.Zone;
@@ -107,15 +112,6 @@ public class CardFactoryUtil {
card.addIntrinsicKeyword("Haste");
card.setUnearthed(true);
}
-
- @Override
- public boolean canPlayAI(Player aiPlayer) {
- PhaseHandler phase = sourceCard.getGame().getPhaseHandler();
- if (phase.getPhase().isAfter(PhaseType.MAIN1) || !phase.isPlayerTurn(getActivatingPlayer())) {
- return false;
- }
- return ComputerUtilCost.canPayCost(this, getActivatingPlayer());
- }
}
final AbilityActivated unearth = new AbilityUnearth(sourceCard, cost, null);
@@ -308,13 +304,6 @@ public class CardFactoryUtil {
return sourceCard.getOwner().canCastSorcery();
}
- @Override
- public boolean canPlayAI(Player aiPlayer) {
- return true;
- // Suspend currently not functional for the AI,
- // seems to be an issue with regaining Priority after Suspension
- }
-
@Override
public void resolve() {
final Game game = sourceCard.getGame();
@@ -2888,23 +2877,6 @@ public class CardFactoryUtil {
card.setEvoked(true);
game.getAction().moveToPlay(card);
}
-
- @Override
- public boolean canPlayAI(Player aiPlayer) {
- final Game game = card.getGame();
- if (!SpellPermanent.checkETBEffects(card, aiPlayer)) {
- return false;
- }
- // Wait for Main2 if possible
- if (game.getPhaseHandler().is(PhaseType.MAIN1)
- && game.getPhaseHandler().isPlayerTurn(aiPlayer)
- && aiPlayer.getManaPool().totalMana() <= 0
- && !ComputerUtil.castPermanentInMain1(aiPlayer, this)) {
- return false;
- }
-
- return super.canPlayAI(aiPlayer);
- }
};
card.removeIntrinsicKeyword(evokeKeyword);
final StringBuilder desc = new StringBuilder();
diff --git a/forge-game/src/main/java/forge/game/cost/CostPayLife.java b/forge-game/src/main/java/forge/game/cost/CostPayLife.java
index 628e03db181..f9c87fa2894 100644
--- a/forge-game/src/main/java/forge/game/cost/CostPayLife.java
+++ b/forge-game/src/main/java/forge/game/cost/CostPayLife.java
@@ -91,9 +91,6 @@ public class CostPayLife extends CostPart {
return true;
}
- /* (non-Javadoc)
- * @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
- */
@Override
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
// TODO Auto-generated method stub
diff --git a/forge-game/src/main/java/forge/game/cost/CostRemoveAnyCounter.java b/forge-game/src/main/java/forge/game/cost/CostRemoveAnyCounter.java
index a8d8008ecd6..f4f7c48d00f 100644
--- a/forge-game/src/main/java/forge/game/cost/CostRemoveAnyCounter.java
+++ b/forge-game/src/main/java/forge/game/cost/CostRemoveAnyCounter.java
@@ -18,6 +18,7 @@
package forge.game.cost;
import com.google.common.base.Predicate;
+
import forge.ai.ComputerUtil;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
@@ -128,9 +129,6 @@ public class CostRemoveAnyCounter extends CostPartWithList {
return sb.toString();
}
- /* (non-Javadoc)
- * @see forge.card.cost.CostPartWithList#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
- */
@Override
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
final String amount = this.getAmount();
diff --git a/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java b/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java
index 9a083636027..bccc6466cca 100644
--- a/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java
+++ b/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java
@@ -153,9 +153,6 @@ public class CostRemoveCounter extends CostPartWithList {
return true;
}
- /* (non-Javadoc)
- * @see forge.card.cost.CostPartWithList#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
- */
@Override
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
Card source = ability.getHostCard();
diff --git a/forge-game/src/main/java/forge/game/cost/CostTap.java b/forge-game/src/main/java/forge/game/cost/CostTap.java
index 15b2198219c..eaa433aeb39 100644
--- a/forge-game/src/main/java/forge/game/cost/CostTap.java
+++ b/forge-game/src/main/java/forge/game/cost/CostTap.java
@@ -42,42 +42,23 @@ public class CostTap extends CostPart {
@Override
public boolean isRenewable() { return true; }
- /*
- * (non-Javadoc)
- *
- * @see forge.card.cost.CostPart#toString()
- */
@Override
public final String toString() {
return "{T}";
}
- /*
- * (non-Javadoc)
- *
- * @see forge.card.cost.CostPart#refund(forge.Card)
- */
@Override
public final void refund(final Card source) {
source.setTapped(false);
}
- /*
- * (non-Javadoc)
- *
- * @see
- * forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility,
- * forge.Card, forge.Player, forge.card.cost.Cost)
- */
+
@Override
public final boolean canPay(final SpellAbility ability) {
final Card source = ability.getHostCard();
return source.isUntapped() && (!source.isSick() || source.hasKeyword("CARDNAME may activate abilities as though it has haste."));
}
- /* (non-Javadoc)
- * @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
- */
@Override
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
ability.getHostCard().tap();
diff --git a/forge-game/src/main/java/forge/game/cost/CostUntap.java b/forge-game/src/main/java/forge/game/cost/CostUntap.java
index f950c6e1ecd..21cd0d28a2f 100644
--- a/forge-game/src/main/java/forge/game/cost/CostUntap.java
+++ b/forge-game/src/main/java/forge/game/cost/CostUntap.java
@@ -75,9 +75,6 @@ public class CostUntap extends CostPart {
return source.isTapped() && (!source.isSick() || source.hasKeyword("CARDNAME may activate abilities as though it has haste."));
}
- /* (non-Javadoc)
- * @see forge.card.cost.CostPart#payAI(forge.card.cost.PaymentDecision, forge.game.player.AIPlayer, forge.card.spellability.SpellAbility, forge.Card)
- */
@Override
public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
ability.getHostCard().untap();
diff --git a/forge-game/src/main/java/forge/game/player/PlayerControllerAi.java b/forge-game/src/main/java/forge/game/player/PlayerControllerAi.java
index 0d8831025f8..9e22e9dada0 100644
--- a/forge-game/src/main/java/forge/game/player/PlayerControllerAi.java
+++ b/forge-game/src/main/java/forge/game/player/PlayerControllerAi.java
@@ -6,6 +6,7 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
+
import forge.ai.*;
import forge.ai.ability.CharmAi;
import forge.card.ColorSet;
@@ -35,6 +36,7 @@ import forge.game.zone.ZoneType;
import forge.item.PaperCard;
import forge.util.Aggregates;
import forge.util.MyRandom;
+
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
@@ -175,7 +177,7 @@ public class PlayerControllerAi extends PlayerController {
}
// There is no way this doTrigger here will have the same target as stored above
// So it's possible it's making a different decision here than will actually happen
- if (!sa.doTrigger(isMandatory, player)) {
+ if (!brains.doTrigger(sa, isMandatory)) {
ret = false;
}
if (storeChoices) {
@@ -265,12 +267,12 @@ public class PlayerControllerAi extends PlayerController {
if (mayChooseNewTargets) {
if (copySA instanceof Spell) {
Spell spell = (Spell) copySA;
- if (!spell.canPlayFromEffectAI(player, true, true)) {
+ if (AiPlayDecision.WillPlay != ((PlayerControllerAi)player.getController()).getAi().canPlayFromEffectAI(spell, true, true)) {
return; // is this legal at all?
}
}
else {
- copySA.canPlayAI(player);
+ getAi().canPlaySa(copySA);
}
}
ComputerUtil.playSpellAbilityForFree(player, copySA);
@@ -279,7 +281,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public void playSpellAbilityNoStack(SpellAbility effectSA, boolean canSetupTargets) {
if (canSetupTargets)
- effectSA.doTrigger(true, player); // first parameter does not matter, since return value won't be used
+ brains.doTrigger(effectSA, true); // first parameter does not matter, since return value won't be used
ComputerUtil.playNoStack(player, effectSA, game);
}
@@ -344,7 +346,7 @@ public class PlayerControllerAi extends PlayerController {
*/
@Override
public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question) {
- return ReplacementEffect.aiShouldRun(replacementEffect, effectSA, player);
+ return brains.aiShouldRun(replacementEffect, effectSA);
}
@Override
@@ -629,7 +631,7 @@ public class PlayerControllerAi extends PlayerController {
Player targetingPlayer = AbilityUtils.getDefinedPlayers(host, sa.getParam("TargetingPlayer"), sa).get(0);
targetingPlayer.getController().chooseTargetsFor(sa);
} else {
- sa.doTrigger(isMandatory, player);
+ brains.doTrigger(sa, isMandatory);
}
}
@@ -645,7 +647,7 @@ public class PlayerControllerAi extends PlayerController {
boolean noManaCost = tgtSA.hasParam("WithoutManaCost");
if (tgtSA instanceof Spell) { // Isn't it ALWAYS a spell?
Spell spell = (Spell) tgtSA;
- if (spell.canPlayFromEffectAI(player, !optional, noManaCost) || !optional) {
+ if (brains.canPlayFromEffectAI(spell, !optional, noManaCost) == AiPlayDecision.WillPlay || !optional) {
if (noManaCost) {
ComputerUtil.playSpellAbilityWithoutPayingManaCost(player, tgtSA, game);
} else {
@@ -664,7 +666,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public boolean chooseTargetsFor(SpellAbility currentAbility) {
- return currentAbility.doTrigger(true, player);
+ return brains.doTrigger(currentAbility, true);
}
@Override
diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java
index 20dac254949..ea66c34b413 100644
--- a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java
+++ b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java
@@ -19,14 +19,9 @@ package forge.game.replacement;
import forge.game.Game;
import forge.game.TriggerReplacementBase;
-import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
-import forge.game.card.CardFactoryUtil;
import forge.game.phase.PhaseType;
-import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
-import forge.game.zone.ZoneType;
-import forge.util.Expressions;
import java.util.List;
import java.util.Map;
@@ -50,7 +45,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
public final boolean hasRun() {
return this.hasRun;
}
-
+
/**
* Instantiates a new replacement effect.
*
@@ -69,60 +64,11 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
* Checks if is secondary.
*
* @return true, if is secondary
- */
+ */
public final boolean isSecondary() {
return this.getMapParams().containsKey("Secondary");
}
-
- /**
- * Ai should run.
- *
- * @param sa the sa
- * @param ai
- * @return true, if successful
- */
- public final static boolean aiShouldRun(final ReplacementEffect effect, final SpellAbility sa, Player ai) {
- if (effect.getMapParams().containsKey("AICheckSVar")) {
- System.out.println("aiShouldRun?" + sa);
- final String svarToCheck = effect.getMapParams().get("AICheckSVar");
- String comparator = "GE";
- int compareTo = 1;
-
- if (effect.getMapParams().containsKey("AISVarCompare")) {
- final String fullCmp = effect.getMapParams().get("AISVarCompare");
- comparator = fullCmp.substring(0, 2);
- final String strCmpTo = fullCmp.substring(2);
- try {
- compareTo = Integer.parseInt(strCmpTo);
- } catch (final Exception ignored) {
- if (sa == null) {
- compareTo = CardFactoryUtil.xCount(effect.hostCard, effect.hostCard.getSVar(strCmpTo));
- } else {
- compareTo = AbilityUtils.calculateAmount(effect.hostCard, effect.hostCard.getSVar(strCmpTo), sa);
- }
- }
- }
-
- int left = 0;
-
- if (sa == null) {
- left = CardFactoryUtil.xCount(effect.hostCard, effect.hostCard.getSVar(svarToCheck));
- } else {
- left = AbilityUtils.calculateAmount(effect.hostCard, svarToCheck, sa);
- }
- System.out.println("aiShouldRun?" + left + comparator + compareTo);
- if (Expressions.compare(left, comparator, compareTo)) {
- return true;
- }
- } else if (effect.getMapParams().containsKey("AICheckDredge")) {
- return ai.getCardsIn(ZoneType.Library).size() > 8 || ai.isCardInPlay("Laboratory Maniac");
- } else if (sa != null && sa.doTrigger(false, ai)) {
- return true;
- }
-
- return false;
- }
-
+
/**
* Sets the checks for run.
*
@@ -133,7 +79,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
this.hasRun = hasRun;
}
- /**
+ /**
* Can replace.
*
* @param runParams
diff --git a/forge-game/src/main/java/forge/game/spellability/AbilitySub.java b/forge-game/src/main/java/forge/game/spellability/AbilitySub.java
index b845ac74703..aa9cec63305 100644
--- a/forge-game/src/main/java/forge/game/spellability/AbilitySub.java
+++ b/forge-game/src/main/java/forge/game/spellability/AbilitySub.java
@@ -17,7 +17,6 @@
*/
package forge.game.spellability;
-import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityFactory;
import forge.game.ability.ApiType;
import forge.game.ability.SpellAbilityEffect;
@@ -28,7 +27,6 @@ import forge.game.ability.effects.ManaReflectedEffect;
import forge.game.card.Card;
import forge.game.card.CardFactory;
import forge.game.cost.Cost;
-import forge.game.player.Player;
import java.util.Map;
@@ -81,14 +79,7 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab
private final SpellAbilityEffect effect;
- private final SpellAbilityAi ai;
- /**
- * @return the ai
- */
- public SpellAbilityAi getAi() {
- return ai;
- }
public AbilitySub(ApiType api0, final Card ca, final TargetRestrictions tgt, Map params0) {
super(ca, Cost.Zero);
@@ -96,7 +87,6 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab
api = api0;
mapParams.putAll(params0);
- ai = api.getAi();
effect = api.getSpellEffect();
if (effect instanceof ManaEffect || effect instanceof ManaReflectedEffect) {
@@ -120,18 +110,8 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab
return effect.getStackDescriptionWithSubs(mapParams, this);
}
- @Override
- public boolean canPlayAI(Player aiPlayer) {
- return ai.canPlayAIWithSubs(aiPlayer, this);
- }
-
@Override
public void resolve() {
effect.resolve(this);
}
-
- @Override
- public boolean doTrigger(final boolean mandatory, Player aiPlayer) {
- return ai.doTriggerAI(aiPlayer, this, mandatory);
- }
}
diff --git a/forge-game/src/main/java/forge/game/spellability/Spell.java b/forge-game/src/main/java/forge/game/spellability/Spell.java
index fae23f90252..15b5f352089 100644
--- a/forge-game/src/main/java/forge/game/spellability/Spell.java
+++ b/forge-game/src/main/java/forge/game/spellability/Spell.java
@@ -19,14 +19,11 @@ package forge.game.spellability;
import forge.game.Game;
import forge.game.card.Card;
-import forge.game.card.CardFactoryUtil;
-import forge.game.card.CardLists;
import forge.game.cost.Cost;
import forge.game.cost.CostPayment;
import forge.game.player.Player;
import forge.game.staticability.StaticAbility;
import forge.game.zone.ZoneType;
-import forge.util.Expressions;
import java.util.ArrayList;
import java.util.List;
@@ -124,46 +121,6 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
return true;
}
- /** {@inheritDoc} */
- @Override
- public boolean canPlayAI(Player aiPlayer) {
- final Card card = this.getHostCard();
- final Game game = getActivatingPlayer().getGame();
- if (card.getSVar("NeedsToPlay").length() > 0) {
- final String needsToPlay = card.getSVar("NeedsToPlay");
- List list = game.getCardsIn(ZoneType.Battlefield);
-
- list = CardLists.getValidCards(list, needsToPlay.split(","), card.getController(), card);
- if (list.isEmpty()) {
- return false;
- }
- }
- if (card.getSVar("NeedsToPlayVar").length() > 0) {
- final String needsToPlay = card.getSVar("NeedsToPlayVar");
- int x = 0;
- int y = 0;
- String sVar = needsToPlay.split(" ")[0];
- String comparator = needsToPlay.split(" ")[1];
- String compareTo = comparator.substring(2);
- try {
- x = Integer.parseInt(sVar);
- } catch (final NumberFormatException e) {
- x = CardFactoryUtil.xCount(card, card.getSVar(sVar));
- }
- try {
- y = Integer.parseInt(compareTo);
- } catch (final NumberFormatException e) {
- y = CardFactoryUtil.xCount(card, card.getSVar(compareTo));
- }
- if (!Expressions.compare(x, comparator, y)) {
- return false;
- }
- }
-
- return super.canPlayAI(aiPlayer);
- }
-
-
/** {@inheritDoc} */
@Override
public final Object clone() {
@@ -180,21 +137,6 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
public boolean isAbility() { return false; }
- /**
- *
- * canPlayFromEffectAI.
- *
- *
- * @param mandatory
- * can the controller chose not to play the spell
- * @param withOutManaCost
- * is the spell cast without paying mana
- * @return a boolean.
- */
- public boolean canPlayFromEffectAI(Player aiPlayer, boolean mandatory, boolean withOutManaCost) {
- return canPlayAI(aiPlayer);
- }
-
/**
* @return the castFaceDown
*/
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
index 96d053deb4c..b47c40d2b64 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
@@ -210,32 +210,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
*/
public abstract void resolve();
- /**
- *
- * canPlayAI.
- *
- *
- * @return a boolean.
- */
- public /*final*/ boolean canPlayAI(Player aiPlayer) {
- return true;
- }
-
- // This should be overridden by ALL AFs
- /**
- *
- * doTrigger.
- *
- *
- * @param mandatory
- * a boolean.
- * @param ai TODO
- * @return a boolean.
- */
- public boolean doTrigger(final boolean mandatory, Player ai) {
- return false;
- }
-
/**
*
* Getter for the field multiKickerManaCost.
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellPermanent.java b/forge-game/src/main/java/forge/game/spellability/SpellPermanent.java
index cc79bbd0cf8..6898b737ce9 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellPermanent.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellPermanent.java
@@ -17,31 +17,11 @@
*/
package forge.game.spellability;
-import com.google.common.collect.Iterables;
-import forge.ai.ComputerUtil;
-import forge.ai.ComputerUtilCost;
-import forge.ai.ComputerUtilMana;
-import forge.card.mana.ManaCost;
-import forge.game.Game;
-import forge.game.GlobalRuleChange;
-import forge.game.ability.AbilityFactory;
-import forge.game.ability.ApiType;
-import forge.game.card.Card;
-import forge.game.card.CardLists;
-import forge.game.card.CardPredicates;
-import forge.game.cost.Cost;
-import forge.game.phase.PhaseType;
-import forge.game.player.Player;
-import forge.game.replacement.ReplaceMoved;
-import forge.game.replacement.ReplacementEffect;
-import forge.game.trigger.Trigger;
-import forge.game.trigger.TriggerType;
-import forge.game.zone.ZoneType;
import org.apache.commons.lang3.StringUtils;
-import java.security.InvalidParameterException;
-import java.util.List;
-import java.util.Map;
+import forge.game.card.Card;
+import forge.game.cost.Cost;
+import forge.game.zone.ZoneType;
/**
*
@@ -84,282 +64,6 @@ public class SpellPermanent extends Spell {
} // Spell_Permanent()
- /** {@inheritDoc} */
- @Override
- public boolean canPlayAI(Player aiPlayer) {
-
- final Card card = this.getHostCard();
- ManaCost mana = this.getPayCosts().getTotalMana();
- final Game game = aiPlayer.getGame();
- if (mana.countX() > 0) {
- // Set PayX here to maximum value.
- final int xPay = ComputerUtilMana.determineLeftoverMana(this, aiPlayer);
- if (xPay <= 0) {
- return false;
- }
- card.setSVar("PayX", Integer.toString(xPay));
- }
- // Prevent the computer from summoning Ball Lightning type creatures after attacking
- if (card.hasKeyword("At the beginning of the end step, sacrifice CARDNAME.")
- && (game.getPhaseHandler().isPlayerTurn(aiPlayer.getOpponent())
- || game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS))) {
- return false;
- }
-
- // Prevent the computer from summoning Ball Lightning type creatures after attacking
- if (card.hasStartOfKeyword("You may cast CARDNAME as though it had flash. If")
- && !card.getController().couldCastSorcery(this)) {
- return false;
- }
-
- // Wait for Main2 if possible
- if (game.getPhaseHandler().is(PhaseType.MAIN1)
- && game.getPhaseHandler().isPlayerTurn(aiPlayer)
- && aiPlayer.getManaPool().totalMana() <= 0
- && !ComputerUtil.castPermanentInMain1(aiPlayer, this)) {
- return false;
- }
- // save cards with flash for surprise blocking
- if (card.hasKeyword("Flash")
- && (aiPlayer.isUnlimitedHandSize() || aiPlayer.getCardsIn(ZoneType.Hand).size() <= aiPlayer.getMaxHandSize())
- && aiPlayer.getManaPool().totalMana() <= 0
- && (game.getPhaseHandler().isPlayerTurn(aiPlayer)
- || game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
- && !card.hasETBTrigger())
- && !ComputerUtil.castPermanentInMain1(aiPlayer, this)) {
- return false;
- }
-
- return canPlayFromEffectAI(aiPlayer, false, true);
- } // canPlayAI()
-
- /** {@inheritDoc} */
- @Override
- public boolean canPlayFromEffectAI(Player aiPlayer, final boolean mandatory, final boolean withOutManaCost) {
- if (mandatory) {
- return true;
- }
- final Card card = this.getHostCard();
- ManaCost mana = this.getPayCosts().getTotalMana();
- final Cost cost = this.getPayCosts();
-
- if (cost != null) {
- // AI currently disabled for these costs
- if (!ComputerUtilCost.checkLifeCost(aiPlayer, cost, card, 4, null)) {
- return false;
- }
-
- if (!ComputerUtilCost.checkDiscardCost(aiPlayer, cost, card)) {
- return false;
- }
-
- if (!ComputerUtilCost.checkSacrificeCost(aiPlayer, cost, card)) {
- return false;
- }
-
- if (!ComputerUtilCost.checkRemoveCounterCost(cost, card)) {
- return false;
- }
- }
-
- // check on legendary
- if (card.isType("Legendary")
- && !aiPlayer.getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule)) {
- final List list = aiPlayer.getCardsIn(ZoneType.Battlefield);
- if (Iterables.any(list, CardPredicates.nameEquals(card.getName()))) {
- return false;
- }
- }
- if (card.isPlaneswalker()) {
- List list = aiPlayer.getCardsIn(ZoneType.Battlefield);
- list = CardLists.filter(list, CardPredicates.Presets.PLANEWALKERS);
-
- for (int i = 0; i < list.size(); i++) {
- List type = card.getType();
- final String subtype = type.get(type.size() - 1);
- final List cl = CardLists.getType(list, subtype);
-
- if (cl.size() > 0) {
- return false;
- }
- }
- }
- if (card.isType("World")) {
- List list = aiPlayer.getCardsIn(ZoneType.Battlefield);
- list = CardLists.getType(list, "World");
- if (list.size() > 0) {
- return false;
- }
- }
-
- if (card.isCreature() && (card.getNetDefense() <= 0) && !card.hasStartOfKeyword("etbCounter")
- && mana.countX() == 0 && !card.hasETBTrigger()
- && !card.hasETBReplacement()) {
- return false;
- }
-
- if (!SpellPermanent.checkETBEffects(card, this, null, aiPlayer)) {
- return false;
- }
- if (ComputerUtil.damageFromETB(aiPlayer, card) >= aiPlayer.getLife() && aiPlayer.canLoseLife()) {
- return false;
- }
- return super.canPlayAI(aiPlayer);
- }
-
- public static boolean checkETBEffects(final Card card, final Player ai) {
- return checkETBEffects(card, null, null, ai);
- }
-
- public static boolean checkETBEffects(final Card card, final SpellAbility sa, final ApiType api, final Player ai) {
- boolean rightapi = false;
- final Game game = ai.getGame();
-
- if (card.isCreature()
- && game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noCreatureETBTriggers)) {
- return api == null;
- }
-
- // Trigger play improvements
- for (final Trigger tr : card.getTriggers()) {
- // These triggers all care for ETB effects
-
- final Map params = tr.getMapParams();
- if (tr.getMode() != TriggerType.ChangesZone) {
- continue;
- }
-
- if (!params.get("Destination").equals(ZoneType.Battlefield.toString())) {
- continue;
- }
-
- if (params.containsKey("ValidCard")) {
- if (!params.get("ValidCard").contains("Self")) {
- continue;
- }
- if (params.get("ValidCard").contains("notkicked")) {
- if (sa.isKicked()) {
- continue;
- }
- } else if (params.get("ValidCard").contains("kicked")) {
- if (params.get("ValidCard").contains("kicked ")) { // want a specific kicker
- String s = params.get("ValidCard").split("kicked ")[1];
- if ( "1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
- if ( "2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
- } else if (!sa.isKicked()) {
- continue;
- }
- }
- }
-
- if (!tr.requirementsCheck(game)) {
- continue;
- }
-
- if (tr.getOverridingAbility() != null) {
- // Abilities yet
- continue;
- }
-
- // if trigger is not mandatory - no problem
- if (params.get("OptionalDecider") != null) {
- continue;
- }
-
- // Maybe better considerations
- final String execute = params.get("Execute");
- if (execute == null) {
- continue;
- }
- final SpellAbility exSA = AbilityFactory.getAbility(card.getSVar(execute), card);
-
- if (api != null) {
- if (exSA.getApi() != api) {
- continue;
- } else {
- rightapi = true;
- }
- }
-
- if (sa != null) {
- exSA.setActivatingPlayer(sa.getActivatingPlayer());
- }
- else {
- exSA.setActivatingPlayer(ai);
- }
- exSA.setTrigger(true);
-
- // Run non-mandatory trigger.
- // These checks only work if the Executing SpellAbility is an Ability_Sub.
- if ((exSA instanceof AbilitySub) && !exSA.doTrigger(false, ai)) {
- // AI would not run this trigger if given the chance
- return false;
- }
- }
- if (api != null && !rightapi) {
- return false;
- }
-
- // Replacement effects
- for (final ReplacementEffect re : card.getReplacementEffects()) {
- // These Replacements all care for ETB effects
-
- final Map params = re.getMapParams();
- if (!(re instanceof ReplaceMoved)) {
- continue;
- }
-
- if (!params.get("Destination").equals(ZoneType.Battlefield.toString())) {
- continue;
- }
-
- if (params.containsKey("ValidCard")) {
- if (!params.get("ValidCard").contains("Self")) {
- continue;
- }
- if (params.get("ValidCard").contains("notkicked")) {
- if (sa.isKicked()) {
- continue;
- }
- } else if (params.get("ValidCard").contains("kicked")) {
- if (params.get("ValidCard").contains("kicked ")) { // want a specific kicker
- String s = params.get("ValidCard").split("kicked ")[1];
- if ( "1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
- if ( "2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
- } else if (!sa.isKicked()) { // otherwise just any must be present
- continue;
- }
- }
- }
-
- if (!re.requirementsCheck(game)) {
- continue;
- }
- final SpellAbility exSA = re.getOverridingAbility();
-
- if (exSA != null) {
- if (sa != null) {
- exSA.setActivatingPlayer(sa.getActivatingPlayer());
- }
- else {
- exSA.setActivatingPlayer(ai);
- }
-
- if (exSA.getActivatingPlayer() == null) {
- throw new InvalidParameterException("Executing SpellAbility for Replacement Effect has no activating player");
- }
- }
-
- // ETBReplacement uses overriding abilities.
- // These checks only work if the Executing SpellAbility is an Ability_Sub.
- if (exSA != null && (exSA instanceof AbilitySub) && !exSA.doTrigger(false, ai)) {
- return false;
- }
- }
-
- return true;
- }
-
/** {@inheritDoc} */
@Override
public void resolve() {
diff --git a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java
index 88f8dc9c914..f7e86c6a82b 100644
--- a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java
+++ b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java
@@ -33,6 +33,10 @@ public class WrappedAbility extends Ability implements ISpellAbility {
sa = sa0;
decider = decider0;
}
+
+ public SpellAbility getWrappedAbility() {
+ return sa;
+ }
@Override
@@ -131,21 +135,11 @@ public class WrappedAbility extends Ability implements ISpellAbility {
return sa.canPlay();
}
- @Override
- public boolean canPlayAI(Player aiPlayer) {
- return sa.canPlayAI(aiPlayer);
- }
-
@Override
public SpellAbility copy() {
return sa.copy();
}
- @Override
- public boolean doTrigger(final boolean mandatory, Player ai) {
- return sa.doTrigger(mandatory, ai);
- }
-
@Override
public Player getActivatingPlayer() {
return sa.getActivatingPlayer();
diff --git a/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java
index e72a50377a6..9ac2f2c9ca8 100644
--- a/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java
+++ b/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java
@@ -488,7 +488,8 @@ public class PlayerControllerForTests extends PlayerController {
Player targetingPlayer = AbilityUtils.getDefinedPlayers(host, sa.getParam("TargetingPlayer"), sa).get(0);
targetingPlayer.getController().chooseTargetsFor(sa);
} else {
- sa.doTrigger(isMandatory, player);
+ // this code is no longer possible!
+ // sa.doTrigger(isMandatory, player);
}
}
@@ -505,7 +506,8 @@ public class PlayerControllerForTests extends PlayerController {
boolean noManaCost = tgtSA.hasParam("WithoutManaCost");
if (tgtSA instanceof Spell) { // Isn't it ALWAYS a spell?
Spell spell = (Spell) tgtSA;
- if (spell.canPlayFromEffectAI(player, !optional, noManaCost) || !optional) {
+ // if (spell.canPlayFromEffectAI(player, !optional, noManaCost) || !optional) { -- could not save this part
+ if (spell.canPlay() || !optional) {
if (noManaCost) {
ComputerUtil.playSpellAbilityWithoutPayingManaCost(player, tgtSA, game);
} else {
@@ -526,7 +528,9 @@ public class PlayerControllerForTests extends PlayerController {
@Override
public boolean chooseTargetsFor(SpellAbility currentAbility) {
- return currentAbility.doTrigger(true, player);
+ // no longer possible to run AI's methods on SpellAbility
+ // return currentAbility.doTrigger(true, player);
+ return false;
}
@Override