diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index acc9fa5c19e..79a46f38a51 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -137,8 +137,9 @@ public class ComputerUtilCost { * the source * @return true, if successful */ + public static boolean checkDiscardCost(final Player ai, final Cost cost, final Card source, SpellAbility sa) { - if (cost == null) { + if (cost == null || source.hasSVar("AISkipDiscardCostCheck") /* FIXME: should not be needed! */ ) { return true; } diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java index ef31b6b172a..89ae2a545ad 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -139,6 +139,28 @@ public class ChangeZoneAi extends SpellAbilityAi { } } return false; + } else if (aiLogic.equals("BestCard")) { + CardCollectionView choices = ai.getGame().getCardsIn(ZoneType.listValueOf(sa.getParam("Origin"))); + choices = CardLists.getValidCards(choices, sa.getParam("ChangeType"), host.getController(), host, sa); + if (!choices.isEmpty()) { + return true; + } + } else if (aiLogic.startsWith("DiscardAllAndRetExiled")) { + int numExiledWithSrc = CardLists.filter(ai.getCardsIn(ZoneType.Exile), CardPredicates.isExiledWith(host)).size(); + int curHandSize = ai.getCardsIn(ZoneType.Hand).size(); + + // minimum card advantage unless the hand will be fully reloaded + int minAdv = aiLogic.contains(".minAdv") ? Integer.parseInt(aiLogic.substring(aiLogic.indexOf(".minAdv") + 7)) : 0; + + if (numExiledWithSrc > curHandSize) { + if (ComputerUtil.predictThreatenedObjects(ai, sa, true).contains(host)) { + // Try to gain some card advantage if the card will die anyway + // TODO: ideally, should evaluate the hand value and not discard good hands to it + return true; + } + } + + return (curHandSize + minAdv - 1 < numExiledWithSrc) || (numExiledWithSrc >= ai.getMaxHandSize()); } return super.checkAiLogic(ai, sa, aiLogic); @@ -177,8 +199,13 @@ public class ChangeZoneAi extends SpellAbilityAi { return sa.isTargetNumberValid(); // Pre-targeted in checkAiLogic } else if (aiLogic.equals("Ashiok")) { return true; // If checkAiLogic returns true, then we should be good to go + } else if (aiLogic.equals("BestCard")) { + return true; // If checkAiLogic returns true, then we should be good to go + } else if (aiLogic.startsWith("DiscardAllAndRetExiled")) { + return true; // If checkAiLogic returns true, then we should be good to go } } + if (isHidden(sa)) { return hiddenOriginCanPlayAI(aiPlayer, sa); } diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java index 68b672e49b8..24e732720f4 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java @@ -245,25 +245,8 @@ public class ChangeZoneAllAi extends SpellAbilityAi { && !ComputerUtil.isPlayingReanimator(ai); } } else if (origin.equals(ZoneType.Exile)) { - String logic = sa.getParam("AILogic"); - - if (logic != null && logic.startsWith("DiscardAllAndRetExiled")) { - int numExiledWithSrc = CardLists.filter(ai.getCardsIn(ZoneType.Exile), CardPredicates.isExiledWith(source)).size(); - int curHandSize = ai.getCardsIn(ZoneType.Hand).size(); - - // minimum card advantage unless the hand will be fully reloaded - int minAdv = logic.contains(".minAdv") ? Integer.parseInt(logic.substring(logic.indexOf(".minAdv") + 7)) : 0; - - if (numExiledWithSrc > curHandSize) { - if (ComputerUtil.predictThreatenedObjects(ai, sa, true).contains(source)) { - // Try to gain some card advantage if the card will die anyway - // TODO: ideally, should evaluate the hand value and not discard good hands to it - return true; - } - } - - return (curHandSize + minAdv - 1 < numExiledWithSrc) || (numExiledWithSrc >= ai.getMaxHandSize()); - } + // TODO: nothing to do here at the moment + return false; } else if (origin.equals(ZoneType.Stack)) { // time stop can do something like this: // Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip diff --git a/forge-gui/res/cardsfolder/b/bomat_courier.txt b/forge-gui/res/cardsfolder/b/bomat_courier.txt index 8a90aaed183..34139d140d9 100644 --- a/forge-gui/res/cardsfolder/b/bomat_courier.txt +++ b/forge-gui/res/cardsfolder/b/bomat_courier.txt @@ -7,5 +7,6 @@ T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigExile | TriggerZones$ Batt SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | ExileFaceDown$ True | NoReveal$ True A:AB$ ChangeZone | Cost$ R Discard<1/Hand> Sac<1/CARDNAME> | Defined$ ExiledWith | Origin$ Exile | Destination$ Hand | AILogic$ DiscardAllAndRetExiled.minAdv2 | SpellDescription$ Put all cards exiled with CARDNAME into their owners' hands. DeckNeeds:Color$Red +SVar:AISkipDiscardCostCheck:TRUE AI:RemoveDeck:Random Oracle:Haste\nWhenever Bomat Courier attacks, exile the top card of your library face down. (You can't look at it.)\n{R}, Discard your hand, Sacrifice Bomat Courier: Put all cards exiled with Bomat Courier into their owners' hands. diff --git a/forge-gui/res/cardsfolder/k/kyren_archive.txt b/forge-gui/res/cardsfolder/k/kyren_archive.txt index c76ef687c27..f8296db1720 100644 --- a/forge-gui/res/cardsfolder/k/kyren_archive.txt +++ b/forge-gui/res/cardsfolder/k/kyren_archive.txt @@ -5,5 +5,6 @@ T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigKyrenExile | Tri SVar:TrigKyrenExile:DB$ ChangeZone | Defined$ TopOfLibrary | Origin$ Library | Destination$ Exile | ExileFaceDown$ True A:AB$ ChangeZone | Cost$ 5 Discard<1/Hand> Sac<1/CARDNAME> | Defined$ ExiledWith | Origin$ Exile | Destination$ Hand | AILogic$ DiscardAllAndRetExiled.minAdv2 | SpellDescription$ Put all cards exiled with CARDNAME into their owners' hands. SVar:AIPreference:DiscardCost$Card +SVar:AISkipDiscardCostCheck:TRUE AI:RemoveDeck:Random Oracle:At the beginning of your upkeep, you may exile the top card of your library face down.\n{5}, Discard your hand, Sacrifice Kyren Archive: Put all cards exiled with Kyren Archive into their owner's hand. diff --git a/forge-gui/res/cardsfolder/p/profane_procession_tomb_of_the_dusk_rose.txt b/forge-gui/res/cardsfolder/p/profane_procession_tomb_of_the_dusk_rose.txt index eb9935778b2..d29356bdcc5 100644 --- a/forge-gui/res/cardsfolder/p/profane_procession_tomb_of_the_dusk_rose.txt +++ b/forge-gui/res/cardsfolder/p/profane_procession_tomb_of_the_dusk_rose.txt @@ -12,5 +12,5 @@ Name:Tomb of the Dusk Rose ManaCost:no cost Types:Legendary Land A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color. -A:AB$ ChangeZone | Cost$ 2 W B T | Hidden$ True | Origin$ Exile | Destination$ Battlefield | ChangeType$ Creature.ExiledWithSource | ChangeNum$ 1 | GainControl$ True | Mandatory$ True | StackDescription$ SpellDescription | SpellDescription$ Put a creature card exiled with this permanent onto the battlefield under your control. -Oracle:(Transforms from Profane Procession.)\n{T}: Add one mana of any color.\n{2}{W}{B}, {T}: Put a creature card exiled with this permanent onto the battlefield under your control. +A:AB$ ChangeZone | Cost$ 2 W B T | Hidden$ True | Origin$ Exile | Destination$ Battlefield | ChangeType$ Creature.ExiledWithSource | ChangeNum$ 1 | GainControl$ True | Mandatory$ True | AILogic$ BestCard | StackDescription$ SpellDescription | SpellDescription$ Put a creature card exiled with CARDNAME onto the battlefield under your control. +Oracle:(Transforms from Profane Procession.)\n{T}: Add one mana of any color.\n{2}{W}{B},{T}: Put a creature card exiled with this permanent onto the battlefield under your control.