CardFactoryUtil: Miracle is a Trigger now, and make PlayAi a bit better to check if it can play it

This commit is contained in:
Hanmac
2016-11-18 19:55:05 +00:00
parent 959fd5471e
commit 3c5eb2248b
9 changed files with 119 additions and 172 deletions

View File

@@ -18,7 +18,6 @@
package forge.ai; package forge.ai;
import java.security.InvalidParameterException; import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@@ -974,40 +973,6 @@ public class AiController {
return Boolean.parseBoolean(prop); return Boolean.parseBoolean(prop);
} }
/** Returns the spell ability which has already been played - use it for reference only */
public SpellAbility chooseAndPlaySa(boolean mandatory, boolean withoutPayingManaCost, final SpellAbility... list) {
return chooseAndPlaySa(Arrays.asList(list), mandatory, withoutPayingManaCost);
}
/** Returns the spell ability which has already been played - use it for reference only */
public SpellAbility chooseAndPlaySa(final List<SpellAbility> choices, boolean mandatory, boolean withoutPayingManaCost) {
for (final SpellAbility sa : choices) {
sa.setActivatingPlayer(player);
//Spells
if (sa instanceof Spell) {
if (AiPlayDecision.WillPlay != canPlayFromEffectAI((Spell) sa, mandatory, withoutPayingManaCost)) {
continue;
}
}
else {
if (AiPlayDecision.WillPlay == canPlaySa(sa)) {
continue;
}
}
if (withoutPayingManaCost) {
ComputerUtil.playSpellAbilityWithoutPayingManaCost(player, sa, game);
}
else if (!ComputerUtilCost.canPayCost(sa, player)) {
continue;
}
else {
ComputerUtil.playStack(sa, player, game);
}
return sa;
}
return null;
}
public AiPlayDecision canPlayFromEffectAI(Spell spell, boolean mandatory, boolean withoutPayingManaCost) { public AiPlayDecision canPlayFromEffectAI(Spell spell, boolean mandatory, boolean withoutPayingManaCost) {
final Card card = spell.getHostCard(); final Card card = spell.getHostCard();

View File

@@ -1,5 +1,12 @@
package forge.ai; package forge.ai;
import java.security.InvalidParameterException;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.esotericsoftware.minlog.Log; import com.esotericsoftware.minlog.Log;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
@@ -44,17 +51,10 @@ import forge.game.trigger.WrappedAbility;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.util.Aggregates; import forge.util.Aggregates;
import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
import forge.util.ITriggerEvent; import forge.util.ITriggerEvent;
import forge.util.MyRandom; import forge.util.MyRandom;
import forge.util.collect.FCollection;
import org.apache.commons.lang3.StringUtils; import forge.util.collect.FCollectionView;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.security.InvalidParameterException;
import java.util.*;
/** /**
@@ -79,6 +79,7 @@ public class PlayerControllerAi extends PlayerController {
brains.setUseSimulation(value); brains.setUseSimulation(value);
} }
@Override
public SpellAbility getAbilityToPlay(Card hostCard, List<SpellAbility> abilities, ITriggerEvent triggerEvent) { public SpellAbility getAbilityToPlay(Card hostCard, List<SpellAbility> abilities, ITriggerEvent triggerEvent) {
if (abilities.size() == 0) { if (abilities.size() == 0) {
return null; return null;
@@ -314,11 +315,6 @@ public class PlayerControllerAi extends PlayerController {
ComputerUtil.playNoStack(player, effectSA, game); ComputerUtil.playNoStack(player, effectSA, game);
} }
@Override
public void playMiracle(SpellAbility miracle, Card card) {
getAi().chooseAndPlaySa(false, false, miracle);
}
@Override @Override
public CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave) { public CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave) {
return getAi().chooseCardsToDelve(genericAmount, grave); return getAi().chooseCardsToDelve(genericAmount, grave);
@@ -436,6 +432,7 @@ public class PlayerControllerAi extends PlayerController {
return brains.chooseNumber(sa, title, min, max); return brains.chooseNumber(sa, title, min, max);
} }
@Override
public int chooseNumber(SpellAbility sa, String title, List<Integer> options, Player relatedPlayer) { public int chooseNumber(SpellAbility sa, String title, List<Integer> options, Player relatedPlayer) {
return brains.chooseNumber(sa, title, options, relatedPlayer); return brains.chooseNumber(sa, title, options, relatedPlayer);
} }

View File

@@ -1,14 +1,18 @@
package forge.ai.ability; package forge.ai.ability;
import java.util.List;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import forge.ai.AiPlayDecision; import forge.ai.AiPlayDecision;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
import forge.ai.PlayerControllerAi; import forge.ai.PlayerControllerAi;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.player.Player; import forge.game.player.Player;
@@ -17,64 +21,38 @@ import forge.game.spellability.Spell;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions; import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class PlayAi extends SpellAbilityAi { public class PlayAi extends SpellAbilityAi {
@Override @Override
protected boolean canPlayAI(Player ai, SpellAbility sa) { protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
final Cost abCost = sa.getPayCosts(); final Game game = ai.getGame();
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final Random r = MyRandom.getRandom();
if (abCost != null) {
// AI currently disabled for these costs
if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source)) {
return false;
}
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, null)) {
return false;
}
if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
return false;
}
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
return false;
}
}
// don't use this as a response // don't use this as a response
if (!ai.getGame().getStack().isEmpty()) { if (!game.getStack().isEmpty()) {
return false; return false;
} }
// prevent run-away activations - first time will always return true if (ComputerUtil.preventRunAwayActivations(sa)) {
boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getRestrictions().getNumberTurnActivations()); return false; // prevent infinite loop
}
List<Card> cards; CardCollection cards;
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) { if (tgt != null) {
ZoneType zone = tgt.getZone().get(0); ZoneType zone = tgt.getZone().get(0);
cards = CardLists.getValidCards(ai.getGame().getCardsIn(zone), tgt.getValidTgts(), ai, source, sa); cards = CardLists.getValidCards(game.getCardsIn(zone), tgt.getValidTgts(), ai, source, sa);
if (cards.isEmpty()) { if (cards.isEmpty()) {
return false; return false;
} }
sa.getTargets().add(ComputerUtilCard.getBestAI(cards)); sa.getTargets().add(ComputerUtilCard.getBestAI(cards));
} else if (!sa.hasParam("Valid")) { } else if (!sa.hasParam("Valid")) {
cards = new ArrayList<Card>(AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa)); cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
if (cards.isEmpty()) { if (cards.isEmpty()) {
return false; return false;
} }
} }
return chance; return true;
} }
/** /**
@@ -90,9 +68,7 @@ public class PlayAi extends SpellAbilityAi {
*/ */
@Override @Override
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) { protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) {
if (sa.usesTargeting()) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
return false; return false;
} }
@@ -112,7 +88,8 @@ public class PlayAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean) * @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
*/ */
@Override @Override
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) { public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional,
Player targetedPlayer) {
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() { List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
@@ -122,6 +99,16 @@ public class PlayAi extends SpellAbilityAi {
// timing restrictions still apply // timing restrictions still apply
if (!s.getRestrictions().checkTimingRestrictions(c, s)) if (!s.getRestrictions().checkTimingRestrictions(c, s))
continue; continue;
if (sa.hasParam("PlayCost")) {
Cost abCost;
if ("ManaCost".equals(sa.getParam("PlayCost"))) {
abCost = new Cost(c.getManaCost(), false);
} else {
abCost = new Cost(sa.getParam("PlayCost"), false);
}
spell = (Spell) spell.copyWithDefinedCost(abCost);
}
if( AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, false, true)) { if( AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, false, true)) {
return true; return true;
} }

View File

@@ -213,15 +213,17 @@ public class PlayEffect extends SpellAbilityEffect {
final boolean noManaCost = sa.hasParam("WithoutManaCost"); final boolean noManaCost = sa.hasParam("WithoutManaCost");
if (noManaCost) { if (noManaCost) {
tgtSA = tgtSA.copyWithNoManaCost(); tgtSA = tgtSA.copyWithNoManaCost();
} else if (sa.hasParam("PlayMadness")) { } else if (sa.hasParam("PlayCost")) {
Cost abCost; Cost abCost;
if ("ManaCost".equals(sa.getParam("PlayMadness"))) { if ("ManaCost".equals(sa.getParam("PlayCost"))) {
abCost = new Cost(source.getManaCost(), false); abCost = new Cost(source.getManaCost(), false);
} else { } else {
abCost = new Cost(sa.getParam("PlayMadness"), false); abCost = new Cost(sa.getParam("PlayCost"), false);
} }
tgtSA = tgtSA.copyWithDefinedCost(abCost); tgtSA = tgtSA.copyWithDefinedCost(abCost);
}
if (sa.hasParam("Madness")) {
tgtSA.getHostCard().setMadness(true); tgtSA.getHostCard().setMadness(true);
} }

View File

@@ -211,7 +211,6 @@ public class Card extends GameEntity implements Comparable<Card> {
private NavigableMap<Long, Player> tempControllers = Maps.newTreeMap(); private NavigableMap<Long, Player> tempControllers = Maps.newTreeMap();
private String originalText = "", text = ""; private String originalText = "", text = "";
private Cost miracleCost = null;
private String chosenType = ""; private String chosenType = "";
private List<String> chosenColors; private List<String> chosenColors;
private String namedCard = ""; private String namedCard = "";
@@ -3471,13 +3470,6 @@ public class Card extends GameEntity implements Comparable<Card> {
unearthed = b; unearthed = b;
} }
public final Cost getMiracleCost() {
return miracleCost;
}
public final void setMiracleCost(final Cost cost) {
miracleCost = cost;
}
public final boolean hasSuspend() { public final boolean hasSuspend() {
return suspend; return suspend;
} }

View File

@@ -901,7 +901,7 @@ public class CardFactoryUtil {
} }
if (l[0].startsWith("CommanderCastFromCommandZone")) { if (l[0].startsWith("CommanderCastFromCommandZone")) {
// TODO fix it for multiple commanders // TODO fix it for multiple commanders
// Read SVar CommanderCostRaise from Commander Effect // Read SVar CommanderCostRaise from Commander Effect
Card commeff = CardLists.filter(cc.getCardsIn(ZoneType.Command), Card commeff = CardLists.filter(cc.getCardsIn(ZoneType.Command),
CardPredicates.nameEquals("Commander Effect")).get(0); CardPredicates.nameEquals("Commander Effect")).get(0);
@@ -994,14 +994,14 @@ public class CardFactoryUtil {
return doXMath(cc.getOpponentsGreatestLifeTotal(), m, c); return doXMath(cc.getOpponentsGreatestLifeTotal(), m, c);
} }
if (sq[0].contains("OppsAtLifeTotal")) { if (sq[0].contains("OppsAtLifeTotal")) {
final int lifeTotal = xCount(c, sq[1]); final int lifeTotal = xCount(c, sq[1]);
int number = 0; int number = 0;
for (final Player opp : cc.getOpponents()) { for (final Player opp : cc.getOpponents()) {
if (opp.getLife() == lifeTotal) { if (opp.getLife() == lifeTotal) {
number++; number++;
} }
} }
return doXMath(number, m, c); return doXMath(number, m, c);
} }
// Count$TargetedLifeTotal (targeted player's life total) // Count$TargetedLifeTotal (targeted player's life total)
@@ -1157,13 +1157,13 @@ public class CardFactoryUtil {
} }
if (sq[0].contains("ColorsCtrl")) { if (sq[0].contains("ColorsCtrl")) {
final CardCollectionView list = cc.getCardsIn(ZoneType.Battlefield); final CardCollectionView list = cc.getCardsIn(ZoneType.Battlefield);
int n = 0; int n = 0;
for (final byte col : MagicColor.WUBRG) { for (final byte col : MagicColor.WUBRG) {
if (!CardLists.getColor(list, col).isEmpty()) { if (!CardLists.getColor(list, col).isEmpty()) {
n++; n++;
} }
} }
return doXMath(n, m, c); return doXMath(n, m, c);
} }
@@ -1473,7 +1473,7 @@ public class CardFactoryUtil {
} }
private static CardCollectionView getCardListForXCount(final Card c, final Player cc, final String[] sq) { private static CardCollectionView getCardListForXCount(final Card c, final Player cc, final String[] sq) {
final List<Player> opps = cc.getOpponents(); final List<Player> opps = cc.getOpponents();
CardCollection someCards = new CardCollection(); CardCollection someCards = new CardCollection();
final Game game = c.getGame(); final Game game = c.getGame();
@@ -1503,41 +1503,41 @@ public class CardFactoryUtil {
} }
if (sq[0].contains("OppCtrl")) { if (sq[0].contains("OppCtrl")) {
for (final Player p : opps) { for (final Player p : opps) {
someCards.addAll(p.getZone(ZoneType.Battlefield).getCards()); someCards.addAll(p.getZone(ZoneType.Battlefield).getCards());
} }
} }
if (sq[0].contains("InOppYard")) { if (sq[0].contains("InOppYard")) {
for (final Player p : opps) { for (final Player p : opps) {
someCards.addAll(p.getCardsIn(ZoneType.Graveyard)); someCards.addAll(p.getCardsIn(ZoneType.Graveyard));
} }
} }
if (sq[0].contains("InOppHand")) { if (sq[0].contains("InOppHand")) {
for (final Player p : opps) { for (final Player p : opps) {
someCards.addAll(p.getCardsIn(ZoneType.Hand)); someCards.addAll(p.getCardsIn(ZoneType.Hand));
} }
} }
if (sq[0].contains("InChosenHand")) { if (sq[0].contains("InChosenHand")) {
if (c.getChosenPlayer() != null) { if (c.getChosenPlayer() != null) {
someCards.addAll(c.getChosenPlayer().getCardsIn(ZoneType.Hand)); someCards.addAll(c.getChosenPlayer().getCardsIn(ZoneType.Hand));
} }
} }
if (sq[0].contains("InChosenYard")) { if (sq[0].contains("InChosenYard")) {
if (c.getChosenPlayer() != null) { if (c.getChosenPlayer() != null) {
someCards.addAll(c.getChosenPlayer().getCardsIn(ZoneType.Graveyard)); someCards.addAll(c.getChosenPlayer().getCardsIn(ZoneType.Graveyard));
} }
} }
if (sq[0].contains("OnBattlefield")) { if (sq[0].contains("OnBattlefield")) {
someCards.addAll(game.getCardsIn(ZoneType.Battlefield)); someCards.addAll(game.getCardsIn(ZoneType.Battlefield));
} }
if (sq[0].contains("InAllYards")) { if (sq[0].contains("InAllYards")) {
someCards.addAll(game.getCardsIn(ZoneType.Graveyard)); someCards.addAll(game.getCardsIn(ZoneType.Graveyard));
} }
if (sq[0].contains("SpellsOnStack")) { if (sq[0].contains("SpellsOnStack")) {
@@ -1545,7 +1545,7 @@ public class CardFactoryUtil {
} }
if (sq[0].contains("InAllHands")) { if (sq[0].contains("InAllHands")) {
someCards.addAll(game.getCardsIn(ZoneType.Hand)); someCards.addAll(game.getCardsIn(ZoneType.Hand));
} }
// Count$InTargetedHand (targeted player's cards in hand) // Count$InTargetedHand (targeted player's cards in hand)
@@ -2240,7 +2240,7 @@ public class CardFactoryUtil {
addTriggerAbility(keyword, card, null); addTriggerAbility(keyword, card, null);
} }
else if (keyword.equals("Epic")) { else if (keyword.equals("Epic")) {
addSpellAbility(keyword, card, null); addSpellAbility(keyword, card, null);
} }
else if (keyword.equals("Soulbond")) { else if (keyword.equals("Soulbond")) {
// Setup ETB trigger for card with Soulbond keyword // Setup ETB trigger for card with Soulbond keyword
@@ -2282,6 +2282,9 @@ public class CardFactoryUtil {
else if (keyword.equals("Ingest")) { else if (keyword.equals("Ingest")) {
addTriggerAbility(keyword, card, null); addTriggerAbility(keyword, card, null);
} }
else if (keyword.startsWith("Miracle")) {
addTriggerAbility(keyword, card, null);
}
else if (keyword.equals("Persist")) { else if (keyword.equals("Persist")) {
addTriggerAbility(keyword, card, null); addTriggerAbility(keyword, card, null);
} }
@@ -2981,9 +2984,9 @@ public class CardFactoryUtil {
"Execute$ " + trigPlay + " | Secondary$ True | TriggerDescription$ " + "Execute$ " + trigPlay + " | Secondary$ True | TriggerDescription$ " +
"Play Madness " + ManaCostParser.parse(manacost) + " - " + card.getName(); "Play Madness " + ManaCostParser.parse(manacost) + " - " + card.getName();
final String playMadness = "AB$ Play | Cost$ 0 | Defined$ Self | PlayMadness$ " + manacost + final String playMadness = "AB$ Play | Cost$ 0 | Defined$ Self | PlayCost$ " + manacost +
" | ConditionDefined$ Self | ConditionPresent$ Card.StrictlySelf+inZoneExile" + " | ConditionDefined$ Self | ConditionPresent$ Card.StrictlySelf+inZoneExile" +
" | Optional$ True | SubAbility$ DBWasNotPlayMadness | RememberPlayed$ True"; " | Optional$ True | SubAbility$ DBWasNotPlayMadness | RememberPlayed$ True | Madness$ True";
final String moveToYard = "DB$ ChangeZone | Defined$ Self.StrictlySelf | Origin$ Exile | " + final String moveToYard = "DB$ ChangeZone | Defined$ Self.StrictlySelf | Origin$ Exile | " +
"Destination$ Graveyard | ConditionDefined$ Remembered | ConditionPresent$" + "Destination$ Graveyard | ConditionDefined$ Remembered | ConditionPresent$" +
" Card | ConditionCompare$ EQ0 | SubAbility$ DBMadnessCleanup"; " Card | ConditionCompare$ EQ0 | SubAbility$ DBMadnessCleanup";
@@ -3006,6 +3009,22 @@ public class CardFactoryUtil {
card.setSVar("MeleePump", effect); card.setSVar("MeleePump", effect);
card.setSVar("MeleeX", "TriggeredPlayersDefenders$Amount"); card.setSVar("MeleeX", "TriggeredPlayersDefenders$Amount");
final Trigger trigger = TriggerHandler.parseTrigger(trigStr.toString(), card, intrinsic); final Trigger trigger = TriggerHandler.parseTrigger(trigStr.toString(), card, intrinsic);
final Trigger cardTrigger = card.addTrigger(trigger);
if (!intrinsic) {
kws.addTrigger(cardTrigger);
}
} else if (keyword.startsWith("Miracle")) {
final String[] k = keyword.split(":");
final String manacost = k[1];
final String abStr = "DB$ Play | Defined$ Self | PlayCost$ " + manacost;
final String trigStr = "Mode$ Drawn | ValidCard$ Card.Self | Miracle$ True | Secondary$ True "
+ "| Static$ True | TriggerDescription$ CARDNAME - Miracle";
final Trigger trigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
trigger.setOverridingAbility(AbilityFactory.getAbility(abStr, card));
final Trigger cardTrigger = card.addTrigger(trigger); final Trigger cardTrigger = card.addTrigger(trigger);
if (!intrinsic) { if (!intrinsic) {
kws.addTrigger(cardTrigger); kws.addTrigger(cardTrigger);
@@ -4082,17 +4101,6 @@ public class CardFactoryUtil {
addTriggerAbility(parse, card, null); addTriggerAbility(parse, card, null);
} // madness } // madness
if (hasKeyword(card, "Miracle") != -1) {
final int n = hasKeyword(card, "Miracle");
if (n != -1) {
final String parse = card.getKeywords().get(n).toString();
// card.removeIntrinsicKeyword(parse);
final String[] k = parse.split(":");
card.setMiracleCost(new Cost(k[1], false));
}
} // miracle
if (hasKeyword(card, "Devour") != -1) { if (hasKeyword(card, "Devour") != -1) {
final int n = hasKeyword(card, "Devour"); final int n = hasKeyword(card, "Devour");
addReplacementEffect(card.getKeywords().get(n), card, null); addReplacementEffect(card.getKeywords().get(n), card, null);
@@ -4129,22 +4137,22 @@ public class CardFactoryUtil {
if (hasKeyword(card, "Recover") != -1) { if (hasKeyword(card, "Recover") != -1) {
final String recoverCost = card.getKeywords().get(card.getKeywordPosition("Recover")).split(":")[1]; final String recoverCost = card.getKeywords().get(card.getKeywordPosition("Recover")).split(":")[1];
final String abStr = "AB$ ChangeZone | Cost$ 0 | Defined$ Self" final String abStr = "AB$ ChangeZone | Cost$ 0 | Defined$ Self"
+ " | Origin$ Graveyard | Destination$ Hand | UnlessCost$ " + " | Origin$ Graveyard | Destination$ Hand | UnlessCost$ "
+ recoverCost + " | UnlessPayer$ You | UnlessSwitched$ True" + recoverCost + " | UnlessPayer$ You | UnlessSwitched$ True"
+ " | UnlessResolveSubs$ WhenNotPaid | SubAbility$ RecoverExile"; + " | UnlessResolveSubs$ WhenNotPaid | SubAbility$ RecoverExile";
card.setSVar("RecoverTrig", abStr); card.setSVar("RecoverTrig", abStr);
card.setSVar("RecoverExile", "DB$ ChangeZone | Defined$ Self" card.setSVar("RecoverExile", "DB$ ChangeZone | Defined$ Self"
+ " | Origin$ Graveyard | Destination$ Exile"); + " | Origin$ Graveyard | Destination$ Exile");
String trigObject = card.isCreature() ? "Creature.Other+YouOwn" : "Creature.YouOwn"; String trigObject = card.isCreature() ? "Creature.Other+YouOwn" : "Creature.YouOwn";
String trigArticle = card.isCreature() ? "another" : "a"; String trigArticle = card.isCreature() ? "another" : "a";
String trigStr = "Mode$ ChangesZone | ValidCard$ " + trigObject String trigStr = "Mode$ ChangesZone | ValidCard$ " + trigObject
+ " | Origin$ Battlefield | Destination$ Graveyard | " + " | Origin$ Battlefield | Destination$ Graveyard | "
+ "TriggerZones$ Graveyard | Execute$ RecoverTrig | " + "TriggerZones$ Graveyard | Execute$ RecoverTrig | "
+ "TriggerDescription$ When " + trigArticle + " creature is " + "TriggerDescription$ When " + trigArticle + " creature is "
+ "put into your graveyard from the battlefield, you " + "put into your graveyard from the battlefield, you "
+ "may pay " + recoverCost + ". If you do, return " + "may pay " + recoverCost + ". If you do, return "
+ "CARDNAME from your graveyard to your hand. Otherwise," + "CARDNAME from your graveyard to your hand. Otherwise,"
+ " exile CARDNAME. | Secondary$ True"; + " exile CARDNAME. | Secondary$ True";
final Trigger myTrigger = TriggerHandler.parseTrigger(trigStr, card, true); final Trigger myTrigger = TriggerHandler.parseTrigger(trigStr, card, true);
card.addTrigger(myTrigger); card.addTrigger(myTrigger);
} // Recover } // Recover

View File

@@ -17,6 +17,8 @@
*/ */
package forge.game.trigger; package forge.game.trigger;
import forge.game.Game;
import forge.game.GameStage;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -64,6 +66,13 @@ public class TriggerDrawn extends Trigger {
return false; return false;
} }
} }
if (this.mapParams.containsKey("Miracle")) {
final Game game = this.getHostCard().getGame();
if (number != 1 || game.getAge() == GameStage.Mulligan) {
return false;
}
}
return true; return true;
} }

View File

@@ -268,11 +268,6 @@ public class PlayerControllerForTests extends PlayerController {
return chooseItems(validCards, min); return chooseItems(validCards, min);
} }
@Override
public void playMiracle(SpellAbility miracle, Card card) {
throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!");
}
@Override @Override
public CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave) { public CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave) {
return CardCollection.EMPTY; return CardCollection.EMPTY;

View File

@@ -694,14 +694,6 @@ public class PlayerControllerHuman
return new CardCollection(inp.getSelected()); return new CardCollection(inp.getSelected());
} }
@Override
public void playMiracle(final SpellAbility miracle, final Card card) {
final CardView view = CardView.get(card);
if (getGui().confirm(view, view + " - Drawn. Play for Miracle Cost?")) {
HumanPlay.playSpellAbility(this, player, miracle);
}
}
@Override @Override
public CardCollectionView chooseCardsToDelve(final int genericAmount, final CardCollection grave) { public CardCollectionView chooseCardsToDelve(final int genericAmount, final CardCollection grave) {
final int cardsInGrave = Math.min(genericAmount, grave.size()); final int cardsInGrave = Math.min(genericAmount, grave.size());