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