Compare commits

...

17 Commits

Author SHA1 Message Date
Hans Mackowiak
25b6db1536 fix unused variables 2021-03-25 09:32:20 +01:00
Hans Mackowiak
245586cefa GameAction: removeExiledWith when zone is not exile 2021-03-25 09:32:20 +01:00
Hans Mackowiak
21999488f4 fix usage of CardLists getValidCards using CardTraitBase if able 2021-03-25 09:32:20 +01:00
Hans Mackowiak
d8b9524f80 Card: ChosenType linked to CTB 2021-03-25 09:32:20 +01:00
Hans Mackowiak
3bda4ffe0a AI: fix moved Devotion xcount 2021-03-25 09:32:20 +01:00
Hans Mackowiak
4dba174a6c Card: chosenColors use Table for LinkedAbilities 2021-03-25 09:32:20 +01:00
Hans Mackowiak
f57f6e1c40 fix skyclave apparition and spell queller exiling multiple cards 2021-03-25 09:32:20 +01:00
Hans Mackowiak
c9c974ad00 ExiledWithTable: add way to exile without CardTraitBase for GameState 2021-03-25 09:32:20 +01:00
Hans Mackowiak
18be2c67b6 CardTraitBase: add matchesValidParam and use for Trigger 2021-03-25 09:32:20 +01:00
Michael Kamensky
c27fa18ed7 - Unbreak some AI logic parts 2021-03-25 09:32:20 +01:00
Michael Kamensky
fb7a212597 - Port over the AI logic for Ashiok 2021-03-25 09:32:20 +01:00
Hans Mackowiak
c05f2396a1 Hideaway: fix keyword using ExiledWith 2021-03-25 09:32:20 +01:00
Hans Mackowiak
0ac7fb59e6 CardTraitBase: matchesValid as instance method, better use for isValid 2021-03-25 09:32:20 +01:00
Hans Mackowiak
1db6a772ed cards: update more cards with Remember and ExiledWith 2021-03-25 09:32:20 +01:00
Michael Kamensky
d12527ff21 - Update PC_041415 to work with the changed card script. 2021-03-25 09:32:20 +01:00
Hans Mackowiak
ab5eb24d7a Fix ixalans_binding using Static+ExiledWith 2021-03-25 09:32:20 +01:00
Hans Mackowiak
9f5fd3f35f ExiledWithTable to reduce the need for Remember 2021-03-25 09:32:20 +01:00
179 changed files with 1099 additions and 1220 deletions

View File

@@ -6,10 +6,9 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
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;
@@ -59,6 +58,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.Localizer;
import forge.util.TextUtil;
import forge.util.collect.FCollectionView;
@@ -92,9 +92,15 @@ public class AiCostDecision extends CostDecisionMakerBase {
@Override
public PaymentDecision visit(CostChooseCreatureType cost) {
String choice = player.getController().chooseSomeType("Creature", ability, CardType.getAllCreatureTypes(),
Lists.newArrayList());
return PaymentDecision.type(choice);
Integer amount = cost.convertAmount();
Iterable<String> choices = player.getController().chooseSomeType(
Localizer.getInstance().getMessage("lblCreature"), ability, amount, amount, Lists.newArrayList(CardType.getAllCreatureTypes()));
if (choices == null || Iterables.isEmpty(choices)) {
return null;
}
return PaymentDecision.types(choices);
}
@Override
@@ -601,7 +607,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
public PaymentDecision visit(CostRemoveAnyCounter cost) {
final String amount = cost.getAmount();
final int c = AbilityUtils.calculateAmount(source, amount, ability);
final Card originalHost = ObjectUtils.defaultIfNull(ability.getOriginalHost(), source);
final Card originalHost = ability.getOriginalOrHost();
if (c <= 0) {
return null;

View File

@@ -2256,17 +2256,16 @@ public class ComputerUtil {
return getCardsToDiscardFromOpponent(aiChooser, p, sa, validCards, min, max);
}
public static String chooseSomeType(Player ai, String kindOfType, String logic, Collection<String> validTypes, List<String> invalidTypes) {
if (invalidTypes == null) {
invalidTypes = ImmutableList.of();
}
public static List<String> chooseSomeType(Player ai, String kindOfType, String logic, int min, int max, Collection<String> validTypes) {
if (validTypes == null) {
validTypes = ImmutableList.of();
}
final Game game = ai.getGame();
String chosen = "";
List<String> chosenList = Lists.newArrayList();
if (kindOfType.equals("Card")) {
String chosen = "";
// TODO
// computer will need to choose a type
// based on whether it needs a creature or land,
@@ -2274,24 +2273,25 @@ public class ComputerUtil {
// then, reveal chosenType to Human
if (game.getPhaseHandler().is(PhaseType.UNTAP) && logic == null) { // Storage Matrix
double amount = 0;
for (String type : CardType.getAllCardTypes()) {
if (!invalidTypes.contains(type)) {
CardCollection list = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.isType(type), Presets.TAPPED);
double i = type.equals("Creature") ? list.size() * 1.5 : list.size();
if (i > amount) {
amount = i;
chosen = type;
}
for (String type : validTypes) {
CardCollection list = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.isType(type), Presets.TAPPED);
double i = type.equals("Creature") ? list.size() * 1.5 : list.size();
if (i > amount) {
amount = i;
chosen = type;
}
}
} else if (logic == "MostProminentInComputerDeck") {
chosen = ComputerUtilCard.getMostProminentType(ai.getAllCards(), validTypes);
}
if (StringUtils.isEmpty(chosen)) {
chosen = validTypes.isEmpty() ? "Creature" : Aggregates.random(validTypes);
}
chosenList.add(chosen);
} else if (kindOfType.equals("Creature")) {
String chosen = "";
if (logic != null) {
List <String> valid = Lists.newArrayList(CardType.getAllCreatureTypes());
valid.removeAll(invalidTypes);
List <String> valid = Lists.newArrayList(validTypes);
if (logic.equals("MostProminentOnBattlefield")) {
chosen = ComputerUtilCard.getMostProminentType(game.getCardsIn(ZoneType.Battlefield), valid);
@@ -2302,7 +2302,7 @@ public class ComputerUtil {
else if (logic.equals("MostProminentOppControls")) {
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
chosen = ComputerUtilCard.getMostProminentType(list, valid);
if (!CardType.isACreatureType(chosen) || invalidTypes.contains(chosen)) {
if (!CardType.isACreatureType(chosen) || !validTypes.contains(chosen)) {
list = CardLists.filterControlledBy(game.getCardsInGame(), ai.getOpponents());
chosen = ComputerUtilCard.getMostProminentType(list, valid);
}
@@ -2314,16 +2314,17 @@ public class ComputerUtil {
chosen = ComputerUtilCard.getMostProminentType(ai.getCardsIn(ZoneType.Graveyard), valid);
}
}
if (!CardType.isACreatureType(chosen) || invalidTypes.contains(chosen)) {
chosen = "Sliver";
if (!CardType.isACreatureType(chosen) || !validTypes.contains(chosen)) {
chosen = Iterables.getFirst(validTypes, null);
}
chosenList.add(chosen);
} else if (kindOfType.equals("Basic Land")) {
String chosen = "";
if (logic != null) {
if (logic.equals("MostProminentOppControls")) {
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
List<String> valid = Lists.newArrayList(CardType.getBasicTypes());
valid.removeAll(invalidTypes);
List<String> valid = Lists.newArrayList(validTypes);
chosen = ComputerUtilCard.getMostProminentType(list, valid);
} else if (logic.equals("MostNeededType")) {
@@ -2346,9 +2347,9 @@ public class ComputerUtil {
}
}
else if (logic.equals("ChosenLandwalk")) {
for (Card c : ComputerUtil.getOpponentFor(ai).getLandsInPlay()) {
for (Card c : ai.getWeakestOpponent().getLandsInPlay()) {
for (String t : c.getType()) {
if (!invalidTypes.contains(t) && CardType.isABasicLandType(t)) {
if (validTypes.contains(t) && CardType.isABasicLandType(t)) {
chosen = t;
break;
}
@@ -2357,16 +2358,28 @@ public class ComputerUtil {
}
}
if (!CardType.isABasicLandType(chosen) || invalidTypes.contains(chosen)) {
chosen = "Island";
if (!CardType.isABasicLandType(chosen) || !validTypes.contains(chosen)) {
chosen = Iterables.getFirst(validTypes, null);
}
chosenList.add(chosen);
// the only one with choosing two types is Illusionary Terrain
// and it needs other AI logic
while (chosenList.size() < min) {
validTypes.remove(chosen);
chosen = Iterables.getFirst(validTypes, null);
if (chosen == null) {
return Lists.newArrayList();
}
chosenList.add(chosen);
}
}
else if (kindOfType.equals("Land")) {
String chosen = "";
if (logic != null) {
if (logic.equals("ChosenLandwalk")) {
for (Card c : ComputerUtil.getOpponentFor(ai).getLandsInPlay()) {
for (Card c : ai.getWeakestOpponent().getLandsInPlay()) {
for (String t : c.getType().getLandTypes()) {
if (!invalidTypes.contains(t)) {
if (validTypes.contains(t)) {
chosen = t;
break;
}
@@ -2375,10 +2388,11 @@ public class ComputerUtil {
}
}
if (StringUtils.isEmpty(chosen)) {
chosen = "Island";
chosen = Iterables.getFirst(validTypes, null);
}
chosenList.add(chosen);
}
return chosen;
return chosenList;
}
public static Object vote(Player ai, List<Object> options, SpellAbility sa, Multimap<Object, Player> votes, Player forPlayer) {

View File

@@ -171,12 +171,8 @@ public class ComputerUtilAbility {
return sa;
}
public static Card getAbilitySource(SpellAbility sa) {
return sa.getOriginalHost() != null ? sa.getOriginalHost() : sa.getHostCard();
}
public static String getAbilitySourceName(SpellAbility sa) {
final Card c = getAbilitySource(sa);
final Card c = sa.getOriginalOrHost();
return c != null ? c.getName() : "";
}

View File

@@ -982,7 +982,7 @@ public class ComputerUtilCard {
for(byte c : MagicColor.WUBRG) {
String devotionCode = "Count$Devotion." + MagicColor.toLongString(c);
int devotion = CardFactoryUtil.xCount(sa.getHostCard(), devotionCode);
int devotion = AbilityUtils.calculateAmount(sa.getHostCard(), devotionCode, sa);
if (devotion > curDevotion && !CardLists.filter(hand, CardPredicates.isColor(c)).isEmpty()) {
curDevotion = devotion;
chosenColor = MagicColor.toLongString(c);

View File

@@ -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;
}

View File

@@ -319,8 +319,8 @@ public class ComputerUtilMana {
// Exception: when paying generic mana with Cavern of Souls, prefer the colored mana producing ability
// to attempt to make the spell uncounterable when possible.
if (ComputerUtilAbility.getAbilitySourceName(ma).equals("Cavern of Souls")
&& sa.getHostCard().getType().getCreatureTypes().contains(ma.getHostCard().getChosenType())) {
if (ComputerUtilAbility.getAbilitySourceName(ma).equals("Cavern of Souls") && ma.hasChosenType()
&& sa.getHostCard().getType().getCreatureTypes().contains(ma.getChosenType(0))) {
if (toPay == ManaCostShard.COLORLESS && cost.getUnpaidShards().contains(ManaCostShard.GENERIC)) {
// Deprioritize Cavern of Souls, try to pay generic mana with it instead to use the NoCounter ability
continue;
@@ -414,7 +414,6 @@ public class ComputerUtilMana {
// then apply this one
if (!replaceType.isEmpty()) {
for (SpellAbility saMana : replaceAmount) {
Card card = saMana.getHostCard();
if (saMana.hasParam("ReplaceType")) {
// replace color and colorless
String color = saMana.getParam("ReplaceType");
@@ -436,8 +435,8 @@ public class ComputerUtilMana {
// replace color
String color = saMana.getParam("ReplaceColor");
if ("Chosen".equals(color)) {
if (card.hasChosenColor()) {
color = MagicColor.toShortString(card.getChosenColor());
if (saMana.hasChosenColor()) {
color = MagicColor.toShortString(saMana.getChosenColor());
}
}
if (saMana.hasParam("ReplaceOnly")) {
@@ -489,7 +488,7 @@ public class ComputerUtilMana {
int pAmount = AbilityUtils.calculateAmount(trSA.getHostCard(), trSA.getParamOrDefault("Amount", "1"), trSA);
String produced = trSA.getParam("Produced");
if (produced.equals("Chosen")) {
produced = MagicColor.toShortString(trSA.getHostCard().getChosenColor());
produced = MagicColor.toShortString(trSA.getHostCard().getChosenColor(trSA));
}
manaProduced += " " + StringUtils.repeat(produced, pAmount);
} else if (ApiType.ManaReflected.equals(trSA.getApi())) {

View File

@@ -89,8 +89,7 @@ public abstract class GameState {
private final Map<Card, Integer> markedDamage = new HashMap<>();
private final Map<Card, List<String>> cardToChosenClrs = new HashMap<>();
private final Map<Card, CardCollection> cardToChosenCards = new HashMap<>();
private final Map<Card, String> cardToChosenType = new HashMap<>();
private final Map<Card, String> cardToChosenType2 = new HashMap<>();
private final Map<Card, List<String>> cardToChosenTypes = new HashMap<>();
private final Map<Card, List<String>> cardToRememberedId = new HashMap<>();
private final Map<Card, List<String>> cardToImprintedId = new HashMap<>();
private final Map<Card, List<String>> cardToMergedCards = new HashMap<>();
@@ -341,15 +340,16 @@ public abstract class GameState {
newText.append("|Damage:").append(c.getDamage());
}
if (!c.getChosenColor().isEmpty()) {
newText.append("|ChosenColor:").append(TextUtil.join(c.getChosenColors(), ","));
}
if (!c.getChosenType().isEmpty()) {
newText.append("|ChosenType:").append(c.getChosenType());
}
if (!c.getChosenType2().isEmpty()) {
newText.append("|ChosenType2:").append(c.getChosenType2());
SpellAbility first = c.getFirstSpellAbility();
if (first != null) {
if (first.hasChosenColor()) {
newText.append("|ChosenColor:").append(TextUtil.join(first.getChosenColors(), ","));
}
if (first.hasChosenType()) {
newText.append("|ChosenType:").append(TextUtil.join(first.getChosenType(), ","));
}
}
if (!c.getNamedCard().isEmpty()) {
newText.append("|NamedCard:").append(c.getNamedCard());
}
@@ -638,8 +638,7 @@ public abstract class GameState {
markedDamage.clear();
cardToChosenClrs.clear();
cardToChosenCards.clear();
cardToChosenType.clear();
cardToChosenType2.clear();
cardToChosenTypes.clear();
cardToMergedCards.clear();
cardToScript.clear();
cardAttackMap.clear();
@@ -734,7 +733,7 @@ public abstract class GameState {
if (persistent) {
produced.put("PersistentMana", "True");
}
final AbilityManaPart abMana = new AbilityManaPart(dummy, produced);
final AbilityManaPart abMana = new AbilityManaPart(dummy, null, produced);
game.getAction().invoke(new Runnable() {
@Override
public void run() {
@@ -829,6 +828,7 @@ public abstract class GameState {
Card exiledWith = idToCard.get(Integer.parseInt(id));
c.setExiledWith(exiledWith);
c.setExiledBy(exiledWith.getController());
exiledWith.addExiledWith(c);
}
}
@@ -1087,19 +1087,13 @@ public abstract class GameState {
Card c = entry.getKey();
List<String> colors = entry.getValue();
c.setChosenColors(colors);
c.setChosenColors(colors, c.getFirstSpellAbility());
}
// Chosen type
for (Entry<Card, String> entry : cardToChosenType.entrySet()) {
for (Entry<Card, List<String>> entry : cardToChosenTypes.entrySet()) {
Card c = entry.getKey();
c.setChosenType(entry.getValue());
}
// Chosen type 2
for (Entry<Card, String> entry : cardToChosenType2.entrySet()) {
Card c = entry.getKey();
c.setChosenType2(entry.getValue());
c.setChosenType(entry.getValue(), c.getFirstSpellAbility());
}
// Named card
@@ -1385,9 +1379,7 @@ public abstract class GameState {
} else if (info.startsWith("ChosenColor:")) {
cardToChosenClrs.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(",")));
} else if (info.startsWith("ChosenType:")) {
cardToChosenType.put(c, info.substring(info.indexOf(':') + 1));
} else if (info.startsWith("ChosenType2:")) {
cardToChosenType2.put(c, info.substring(info.indexOf(':') + 1));
cardToChosenTypes.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(",")));
} else if (info.startsWith("ChosenCards:")) {
CardCollection chosen = new CardCollection();
String[] idlist = info.substring(info.indexOf(':') + 1).split(",");

View File

@@ -8,7 +8,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
@@ -80,7 +79,6 @@ import forge.util.MyRandom;
import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
/**
* A prototype for player controller class
*
@@ -545,13 +543,12 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public String chooseSomeType(String kindOfType, SpellAbility sa, Collection<String> validTypes, List<String> invalidTypes, boolean isOptional) {
String chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa.getParam("AILogic"), validTypes, invalidTypes);
if (StringUtils.isBlank(chosen) && !validTypes.isEmpty()) {
chosen = validTypes.iterator().next();
System.err.println("AI has no idea how to choose " + kindOfType +", defaulting to arbitrary element: chosen");
public List<String> chooseSomeType(String kindOfType, SpellAbility sa, int min, int max, List<String> validTypes) {
List<String> chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa.getParam("AILogic"), min, max, validTypes);
if (chosen.isEmpty()) {
return validTypes.subList(0, min);
}
getGame().getAction().notifyOfValue(sa, player, chosen, player);
getGame().getAction().notifyOfValue(sa, player, chosen.toString(), player);
return chosen;
}

View File

@@ -1000,7 +1000,7 @@ public class SpecialCardAi {
return false;
}
String prominentColor = ComputerUtilCard.getMostProminentColor(ai.getCardsIn(ZoneType.Battlefield));
int devotion = CardFactoryUtil.xCount(sa.getHostCard(), "Count$Devotion." + prominentColor);
int devotion = AbilityUtils.calculateAmount(sa.getHostCard(), "Count$Devotion." + prominentColor, sa);
int activationCost = sa.getPayCosts().getTotalMana().getCMC() + (sa.getPayCosts().hasTapCost() ? 1 : 0);
// do not use this SA if devotion to most prominent color is less than its own activation cost + 1 (to actually get advantage)

View File

@@ -401,7 +401,7 @@ public class AnimateAi extends SpellAbilityAi {
// allow ChosenType - overrides anything else specified
if (types.hasSubtype("ChosenType")) {
types.clear();
types.add(source.getChosenType());
types.addAll(sa.getChosenType());
}
final List<String> keywords = Lists.newArrayList();
@@ -432,7 +432,7 @@ public class AnimateAi extends SpellAbilityAi {
if (sa.hasParam("Colors")) {
final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) {
tmpDesc = CardUtil.getShortColorsString(source.getChosenColors());
tmpDesc = CardUtil.getShortColorsString(sa.getChosenColors());
} else {
tmpDesc = CardUtil.getShortColorsString(Lists.newArrayList(Arrays.asList(colors.split(","))));
}

View File

@@ -74,9 +74,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
@Override
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
if (sa.getHostCard() != null && sa.getHostCard().hasSVar("AIPreferenceOverride")) {
Card host = sa.getHostCard();
if (host != null && host.hasSVar("AIPreferenceOverride")) {
// currently used by SacAndUpgrade logic, might need simplification
sa.getHostCard().removeSVar("AIPreferenceOverride");
host.removeSVar("AIPreferenceOverride");
}
if (aiLogic.equals("BeforeCombat")) {
@@ -90,7 +92,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} else if (aiLogic.equals("PriorityOptionalCost")) {
boolean highPriority = false;
// if we have more than one of these in hand, might not be worth waiting for optional cost payment on the additional copy
highPriority |= CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.nameEquals(sa.getHostCard().getName())).size() > 1;
highPriority |= CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.nameEquals(host.getName())).size() > 1;
// if we are in danger in combat, no need to wait to pay the optional cost
highPriority |= ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ai.getGame().getCombat() != null && ComputerUtilCombat.lifeInDanger(ai, ai.getGame().getCombat());
@@ -125,6 +127,40 @@ public class ChangeZoneAi extends SpellAbilityAi {
return true;
} else if (aiLogic.equals("Pongify")) {
return SpecialAiLogic.doPongifyLogic(ai, sa);
} else if (aiLogic.equals("Ashiok")) {
final int loyalty = host.getCurrentLoyalty() - 1;
CardCollectionView choices = new CardCollection();
for (int i = loyalty; i >= 0; i--) {
sa.setXManaCostPaid(i);
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;
}
}
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);
@@ -161,8 +197,15 @@ public class ChangeZoneAi extends SpellAbilityAi {
return SpecialCardAi.MazesEnd.consider(aiPlayer, sa);
} else if (aiLogic.equals("Pongify")) {
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);
}

View File

@@ -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

View File

@@ -20,7 +20,6 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets;
import forge.game.card.CounterEnumType;
import forge.game.combat.Combat;
import forge.game.keyword.Keyword;
import forge.game.phase.PhaseType;
@@ -97,18 +96,6 @@ public class ChooseCardAi extends SpellAbilityAi {
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
}
});
return !choices.isEmpty();
} else if (aiLogic.equals("Ashiok")) {
final int loyalty = host.getCounters(CounterEnumType.LOYALTY) - 1;
for (int i = loyalty; i >= 0; i--) {
sa.setXManaCostPaid(i);
choices = ai.getGame().getCardsIn(choiceZone);
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host, sa);
if (!choices.isEmpty()) {
return true;
}
}
return !choices.isEmpty();
} else if (aiLogic.equals("RandomNonLand")) {
return !CardLists.getValidCards(choices, "Card.nonLand", host.getController(), host, sa).isEmpty();

View File

@@ -8,6 +8,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
@@ -31,6 +32,7 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbilityCantBeCast;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.collect.FCollection;
@@ -228,11 +230,10 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
return allow;
}
//if Iona does prevent from casting, allow it to draw
for (final Card io : player.getCardsIn(ZoneType.Battlefield, "Iona, Shield of Emeria")) {
if (CardUtil.getColors(imprinted).hasAnyColor(MagicColor.fromName(io.getChosenColor()))) {
return allow;
}
SpellAbility firstSpell = imprinted.getFirstSpellAbility();
// check if something would prevent it from casting
if (firstSpell == null || StaticAbilityCantBeCast.cantBeCastAbility(firstSpell, imprinted, owner)) {
return allow;
}
if (dmg == 0) {

View File

@@ -55,15 +55,15 @@ public class GameCopier {
public GameCopier(Game origGame) {
this.origGame = origGame;
}
public Game getOriginalGame() {
return origGame;
}
public Game getCopiedGame() {
return gameObjectMap.getGame();
}
public Game makeCopy() {
return makeCopy(null);
}
@@ -101,7 +101,7 @@ public class GameCopier {
for (Player p : newGame.getPlayers()) {
((PlayerZoneBattlefield) p.getZone(ZoneType.Battlefield)).setTriggers(false);
}
copyGameState(newGame);
for (Player p : newGame.getPlayers()) {
@@ -124,7 +124,6 @@ public class GameCopier {
System.err.println(c + " Remembered: " + o + "/" + o.getClass());
c.addRemembered(o);
}
}
}
for (SpellAbility sa : c.getSpellAbilities()) {
@@ -153,7 +152,7 @@ public class GameCopier {
if (advanceToPhase != null) {
newGame.getPhaseHandler().devAdvanceToPhase(advanceToPhase);
}
return newGame;
}
@@ -180,7 +179,7 @@ public class GameCopier {
}
newGame.getStack().add(newSa);
}
}
}
}
private RegisteredPlayer clonePlayer(RegisteredPlayer p) {
@@ -227,7 +226,7 @@ public class GameCopier {
// TODO: Verify that the above relationships are preserved bi-directionally or not.
}
}
private static final boolean USE_FROM_PAPER_CARD = true;
private Card createCardCopy(Game newGame, Player newOwner, Card c) {
if (c.isToken() && !c.isEmblem()) {
@@ -277,7 +276,7 @@ public class GameCopier {
// TODO: Controllers' list with timestamps should be copied.
zoneOwner = playerMap.get(c.getController());
newCard.setController(zoneOwner, 0);
int setPower = c.getSetPower();
int setToughness = c.getSetToughness();
if (setPower != Integer.MAX_VALUE || setToughness != Integer.MAX_VALUE) {
@@ -286,7 +285,7 @@ public class GameCopier {
}
newCard.setPTBoost(c.getPTBoostTable());
newCard.setDamage(c.getDamage());
newCard.setChangedCardTypes(c.getChangedCardTypesMap());
newCard.setChangedCardKeywords(c.getChangedCardKeywords());
newCard.setChangedCardNames(c.getChangedCardNames());
@@ -335,15 +334,16 @@ public class GameCopier {
if (c.getChosenPlayer() != null) {
newCard.setChosenPlayer(playerMap.get(c.getChosenPlayer()));
}
if (!c.getChosenType().isEmpty()) {
newCard.setChosenType(c.getChosenType());
SpellAbility first = c.getFirstSpellAbility();
if (first != null && first.hasChosenColor()) {
newCard.setChosenColors(Lists.newArrayList(first.getChosenColors()), newCard.getFirstSpellAbility());
}
if (!c.getChosenType2().isEmpty()) {
newCard.setChosenType2(c.getChosenType2());
}
if (c.getChosenColors() != null) {
newCard.setChosenColors(Lists.newArrayList(c.getChosenColors()));
if (first != null && first.hasChosenType()) {
newCard.setChosenColors(Lists.newArrayList(first.getChosenType()), newCard.getFirstSpellAbility());
}
if (!c.getNamedCard().isEmpty()) {
newCard.setNamedCard(c.getNamedCard());
}
@@ -359,7 +359,7 @@ public class GameCopier {
zoneOwner.getZone(zone).add(newCard);
}
}
private static SpellAbility findSAInCard(SpellAbility sa, Card c) {
String saDesc = sa.getDescription();
for (SpellAbility cardSa : c.getAllSpellAbilities()) {
@@ -387,7 +387,7 @@ public class GameCopier {
return find(o);
}
}
public GameObject find(GameObject o) {
GameObject result = cardMap.get(o);
if (result != null)

View File

@@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ImmutableList;
@@ -22,8 +23,10 @@ import forge.game.card.CardView;
import forge.game.card.IHasCardView;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
import forge.game.zone.ZoneType;
import forge.util.Expressions;
import forge.util.collect.FCollection;
/**
* Base class for Triggers,ReplacementEffects and StaticAbilities.
@@ -34,6 +37,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
/** The host card. */
protected Card hostCard;
protected CardState cardState = null;
private StaticAbility grantorStatic = null;
/** The map params. */
protected Map<String, String> originalMapParams = Maps.newHashMap(),
@@ -487,7 +491,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
protected IHasSVars getSVarFallback() {
if (getCardState() != null)
return getCardState();
return getHostCard();
return getOriginalOrHost();
}
@Override
@@ -501,7 +505,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
@Override
public boolean hasSVar(final String name) {
return sVars.containsKey(name) || getSVarFallback().hasSVar(name);
return sVars.containsKey(name) || getOriginalOrHost().hasSVar(name);
}
public Integer getSVarInt(final String name) {
@@ -564,6 +568,19 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
return !getHostCard().equals(getCardState().getCard());
}
public Card getOriginalOrHost() {
return ObjectUtils.defaultIfNull(getOriginalHost(), getHostCard());
}
public StaticAbility getGrantorStatic() {
return grantorStatic;
}
public void setGrantorStatic(final StaticAbility st) {
grantorStatic = st;
}
public Map<String, String> getChangedTextColors() {
return _combineChangedMap(intrinsicChangedTextColors, changedTextColors);
}
@@ -621,4 +638,44 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
// dont use setHostCard to not trigger the not copied parts yet
copy.hostCard = host;
}
public String getChosenColor() {
return getHostCard().getChosenColor(this);
}
public final Iterable<String> getChosenColors() {
return getHostCard().getChosenColors(this);
}
public final boolean hasChosenColor() {
return getHostCard().hasChosenColor(this);
}
public final boolean hasChosenColor(String s) {
return getHostCard().hasChosenColor(s, this);
}
public FCollection<Card> getExiledWith() {
return getHostCard().getExiledWith(this);
}
public boolean isExiledWith(Card object) {
return getHostCard().isExiledWith(object, this);
}
public final Iterable<String> getChosenType() {
return getHostCard().getChosenType(this);
}
public final String getChosenType(int index) {
return getHostCard().getChosenType(this, index);
}
public final boolean hasChosenType() {
return getHostCard().hasChosenType(this);
}
public void setChosenType(final Iterable<String> types) {
getHostCard().setChosenType(types, this);
}
}

View File

@@ -54,13 +54,13 @@ public class ForgeScript {
} else if (property.startsWith("ChosenColor")) {
if (property.endsWith("Source") && isColorlessSource)
return false;
return source.hasChosenColor() && colors.hasAnyColor(MagicColor.fromName(source.getChosenColor()));
return spellAbility.hasChosenColor() && colors.hasAnyColor(MagicColor.fromName(spellAbility.getChosenColor()));
} else if (property.startsWith("AnyChosenColor")) {
if (property.endsWith("Source") && isColorlessSource)
return false;
return source.hasChosenColor()
&& colors.hasAnyColor(ColorSet.fromNames(source.getChosenColors()).getColor());
return spellAbility.hasChosenColor()
&& colors.hasAnyColor(ColorSet.fromNames(spellAbility.getChosenColors()).getColor());
} else if (property.startsWith("non")) {
// ... Other Card types
@@ -71,13 +71,15 @@ public class ForgeScript {
final String svar = property.substring(8);
return cardState.hasSVar(svar);
} else if (property.equals("ChosenType")) {
return cardState.getTypeWithChanges().hasStringType(source.getChosenType());
if (!spellAbility.hasChosenType()) {
return false;
}
return cardState.getTypeWithChanges().hasStringType(spellAbility.getChosenType(0));
} else if (property.equals("IsNotChosenType")) {
return !cardState.getTypeWithChanges().hasStringType(source.getChosenType());
} else if (property.equals("ChosenType2")) {
return cardState.getTypeWithChanges().hasStringType(source.getChosenType2());
} else if (property.equals("IsNotChosenType2")) {
return !cardState.getTypeWithChanges().hasStringType(source.getChosenType2());
if (spellAbility.hasChosenType() && cardState.getTypeWithChanges().hasStringType(spellAbility.getChosenType(0))) {
return false;
}
return true;
} else if (property.startsWith("HasSubtype")) {
final String subType = property.substring(11);
return cardState.getTypeWithChanges().hasSubtype(subType);

View File

@@ -783,6 +783,10 @@ public class Game {
cc.removeEncodedCard(c);
cc.removeRemembered(c);
}
Card exile = c.getExiledWith();
if (exile != null) {
exile.removeExiledWith(c);
}
c.ceaseToExist();
} else {
// return stolen permanents

View File

@@ -419,6 +419,7 @@ public class GameAction {
Card with = c.getExiledWith();
if (with != null) {
with.removeUntilLeavesBattlefield(c);
with.removeExiledWith(c);
}
c.setExiledWith(null);

View File

@@ -19,7 +19,7 @@ public class AbilityApiBased extends AbilityActivated {
effect = api.getSpellEffect();
if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) {
this.setManaPart(new AbilityManaPart(sourceCard, mapParams));
this.setManaPart(new AbilityManaPart(sourceCard, this, mapParams));
this.setUndoable(true); // will try at least
}

View File

@@ -125,6 +125,10 @@ public class AbilityUtils {
c = AbilityUtils.findEffectRoot(hostCard);
}
}
else if (defined.equals("ExiledWith")) {
// should not be needed to check timestamp there
Iterables.addAll(cards, hostCard.getExiledWith(sa));
}
else if (defined.equals("Equipped")) {
c = hostCard.getEquipping();
}
@@ -580,6 +584,10 @@ public class AbilityUtils {
return AbilityUtils.xCount(ability.getOriginalHost(), calcX[1], ability) * multiplier;
}
if (calcX[0].equals("ExiledWith")) {
return CardFactoryUtil.handlePaid(card.getExiledWith(ability), calcX[1], card) * multiplier;
}
if (calcX[0].startsWith("Remembered")) {
// Add whole Remembered list to handlePaid
final CardCollection list = new CardCollection();
@@ -1050,6 +1058,9 @@ public class AbilityUtils {
players.add(s.getActivatingPlayer());
}
}
else if (defined.startsWith("ExiledWith")) {
addPlayer(Lists.newArrayList(card.getExiledWith(sa)), defined, players);
}
else if (defined.startsWith("Remembered")) {
addPlayer(card.getRemembered(), defined, players);
}
@@ -1725,7 +1736,7 @@ public class AbilityUtils {
if (sq[0].contains("HasNumChosenColors")) {
int sum = 0;
for (Card card : AbilityUtils.getDefinedCards(sa.getHostCard(), sq[1], sa)) {
sum += CardUtil.getColors(card).getSharedColors(ColorSet.fromNames(c.getChosenColors())).countColors();
sum += CardUtil.getColors(card).getSharedColors(ColorSet.fromNames(sa.getChosenColors())).countColors();
}
return sum;
}
@@ -1834,6 +1845,33 @@ public class AbilityUtils {
return CardFactoryUtil.doXMath(game.getCounterAddedThisTurn(cType, parts[2], parts[3], c, player, ctb), expr, c);
}
if (l[0].startsWith("ExiledWith")) {
return CardFactoryUtil.doXMath(c.getExiledWith(ctb).size(), expr, c);
}
// Count$DevotionDual.<color name>.<color name>
// Count$Devotion.<color name>
if (sq[0].contains("Devotion")) {
int colorOcurrencices = 0;
String colorName = sq[1];
if (colorName.contains("Chosen")) {
colorName = MagicColor.toShortString(ctb.getChosenColor());
}
byte colorCode = ManaAtom.fromName(colorName);
if (sq[0].equals("DevotionDual")) {
colorCode |= ManaAtom.fromName(sq[2]);
}
for (Card c0 : player.getCardsIn(ZoneType.Battlefield)) {
for (ManaCostShard sh : c0.getManaCost()) {
if ((sh.getColorMask() & colorCode) != 0) {
colorOcurrencices++;
}
}
colorOcurrencices += c0.getAmountOfKeyword("Your devotion to each color and each combination of colors is increased by one.");
}
return CardFactoryUtil.doXMath(colorOcurrencices, expr, c);
}
}
return CardFactoryUtil.xCount(c, s2);
}

View File

@@ -25,7 +25,7 @@ public class SpellApiBased extends Spell {
this.setIntrinsic(true);
if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) {
this.setManaPart(new AbilityManaPart(sourceCard, mapParams));
this.setManaPart(new AbilityManaPart(sourceCard, this, mapParams));
}
if (api.equals(ApiType.ChangeZone) || api.equals(ApiType.ChangeZoneAll)) {

View File

@@ -58,7 +58,7 @@ public class AnimateAllEffect extends AnimateEffectBase {
// allow ChosenType - overrides anything else specified
if (types.hasSubtype("ChosenType")) {
types.clear();
types.add(host.getChosenType());
types.addAll(sa.getChosenType());
}
final List<String> keywords = new ArrayList<>();
@@ -89,7 +89,7 @@ public class AnimateAllEffect extends AnimateEffectBase {
if (sa.hasParam("Colors")) {
final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) {
tmpDesc = CardUtil.getShortColorsString(host.getChosenColors());
tmpDesc = CardUtil.getShortColorsString(sa.getChosenColors());
} else {
tmpDesc = CardUtil.getShortColorsString(new ArrayList<>(Arrays.asList(colors.split(","))));
}

View File

@@ -68,7 +68,7 @@ public class AnimateEffect extends AnimateEffectBase {
// allow ChosenType - overrides anything else specified
if (types.hasSubtype("ChosenType")) {
types.clear();
types.add(source.getChosenType());
types.addAll(sa.getChosenType());
}
final List<String> keywords = Lists.newArrayList();
@@ -100,7 +100,7 @@ public class AnimateEffect extends AnimateEffectBase {
final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) {
tmpDesc = CardUtil.getShortColorsString(source.getChosenColors());
tmpDesc = CardUtil.getShortColorsString(sa.getChosenColors());
} else {
tmpDesc = CardUtil.getShortColorsString(Arrays.asList(colors.split(",")));
}

View File

@@ -2,6 +2,7 @@ package forge.game.ability.effects;
import java.util.List;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.GameCommand;
@@ -72,7 +73,7 @@ public class ChangeTextEffect extends SpellAbilityEffect {
validTypes.addAll(CardType.Constant.CREATURE_TYPES);
kindOfType = "creature";
}
changedTypeWordOriginal = sa.getActivatingPlayer().getController().chooseSomeType(kindOfType, sa, validTypes, Lists.newArrayList());
changedTypeWordOriginal = Iterables.getFirst(sa.getActivatingPlayer().getController().chooseSomeType(kindOfType, sa, 1, 1, validTypes), changedTypeWordsArray[1]);
} else {
changedTypeWordOriginal = changedTypeWordsArray[0];
}
@@ -88,7 +89,8 @@ public class ChangeTextEffect extends SpellAbilityEffect {
validTypes.addAll(CardType.Constant.CREATURE_TYPES);
kindOfType = "creature";
}
changedTypeWordNew = sa.getActivatingPlayer().getController().chooseSomeType(kindOfType, sa, validTypes, forbiddenTypes);
validTypes.removeAll(forbiddenTypes);
changedTypeWordNew = Iterables.getFirst(sa.getActivatingPlayer().getController().chooseSomeType(kindOfType, sa, 1, 1, validTypes), changedTypeWordsArray[1]);
} else {
changedTypeWordNew = changedTypeWordsArray[1];
}

View File

@@ -193,12 +193,9 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
} else {
movedCard = game.getAction().moveTo(destination, c, libraryPos, sa, moveParams);
if (destination == ZoneType.Exile && !c.isToken()) {
Card host = sa.getOriginalHost();
if (host == null) {
host = sa.getHostCard();
}
movedCard.setExiledWith(host);
movedCard.setExiledBy(host.getController());
c.setExiledWith(source);
source.addExiledWith(c, sa);
c.setExiledBy(sa.getActivatingPlayer());
}
if (sa.hasParam("ExileFaceDown")) {
movedCard.turnFaceDown(true);
@@ -250,11 +247,13 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
movedCard.setTimestamp(ts);
}
if (!movedCard.getZone().equals(originZone)) {
Card meld = null;
triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard);
if (c.getMeldedWith() != null) {
Card meld = game.getCardState(c.getMeldedWith(), null);
meld = game.getCardState(c.getMeldedWith(), null);
if (meld != null) {
triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), meld);
}

View File

@@ -685,16 +685,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
movedCard.setTimestamp(ts);
} else {
// might set before card is moved only for nontoken
Card host = null;
if (destination.equals(ZoneType.Exile) && !gameCard.isToken()) {
host = sa.getOriginalHost();
if (host == null) {
host = sa.getHostCard();
}
gameCard.setExiledWith(host);
gameCard.setExiledBy(host.getController());
}
movedCard = game.getAction().moveTo(destination, gameCard, cause);
if (ZoneType.Hand.equals(destination) && ZoneType.Command.equals(originZone.getZoneType())) {
StringBuilder sb = new StringBuilder();
@@ -713,10 +703,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
// might set after card is moved again if something has changed
if (destination.equals(ZoneType.Exile) && !movedCard.isToken()) {
movedCard.setExiledWith(host);
if (host != null) {
movedCard.setExiledBy(host.getController());
}
movedCard.setExiledWith(hostCard);
hostCard.addExiledWith(movedCard, sa);
movedCard.setExiledBy(sa.getActivatingPlayer());
}
if (sa.hasParam("WithCountersType")) {
@@ -728,6 +717,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (sa.hasParam("ExileFaceDown") || sa.hasParam("FaceDown")) {
movedCard.turnFaceDown(true);
}
if (sa.hasParam("ExilePeek")) {
movedCard.addMayLookTemp(player);
}
if (sa.hasParam("Foretold")) {
movedCard.setForetold(true);
movedCard.setForetoldThisTurn(true);
@@ -810,7 +802,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
triggerList.triggerChangesZoneAll(game);
counterTable.triggerCountersPutAll(game);
if (sa.hasParam("AtEOT") && !triggerList.isEmpty()) {
registerDelayedTrigger(sa, sa.getParam("AtEOT"), triggerList.allCards());
}
@@ -1307,12 +1298,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
else if (destination.equals(ZoneType.Exile)) {
movedCard = game.getAction().exile(c, sa, moveParams);
if (!c.isToken()) {
Card host = sa.getOriginalHost();
if (host == null) {
host = sa.getHostCard();
}
movedCard.setExiledWith(host);
movedCard.setExiledBy(host.getController());
movedCard.setExiledWith(source);
source.addExiledWith(movedCard, sa);
movedCard.setExiledBy(sa.getActivatingPlayer());
}
if (sa.hasParam("ExileFaceDown")) {
movedCard.turnFaceDown(true);
@@ -1459,13 +1447,12 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} else if (srcSA.getParam("Destination").equals("Graveyard")) {
movedCard = game.getAction().moveToGraveyard(tgtHost, srcSA, params);
} else if (srcSA.getParam("Destination").equals("Exile")) {
Card host = srcSA.getOriginalHost();
if (host == null) {
host = srcSA.getHostCard();
}
Card hostCard = srcSA.getHostCard();
movedCard = game.getAction().exile(tgtHost, srcSA, params);
movedCard.setExiledWith(host);
movedCard.setExiledBy(host.getController());
movedCard.setExiledWith(hostCard);
hostCard.addExiledWith(movedCard, srcSA);
movedCard.setExiledBy(srcSA.getActivatingPlayer());
} else if (srcSA.getParam("Destination").equals("TopOfLibrary")) {
movedCard = game.getAction().moveToLibrary(tgtHost, srcSA, params);
} else if (srcSA.getParam("Destination").equals("Hand")) {
@@ -1502,4 +1489,5 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
}
}
}

View File

@@ -75,7 +75,7 @@ public class ChooseColorEffect extends SpellAbilityEffect {
if (chosenColors.isEmpty()) {
return;
}
card.setChosenColors(chosenColors);
card.setChosenColors(chosenColors, sa);
p.getGame().getAction().notifyOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), Lang.joinHomogenous(chosenColors)), p);
}
}

View File

@@ -1,16 +1,17 @@
package forge.game.ability.effects;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import forge.card.CardType;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import com.google.common.collect.Lists;
public class ChooseTypeEffect extends SpellAbilityEffect {
@@ -30,14 +31,12 @@ public class ChooseTypeEffect extends SpellAbilityEffect {
public void resolve(SpellAbility sa) {
final Card card = sa.getHostCard();
final String type = sa.getParam("Type");
final List<String> invalidTypes = sa.hasParam("InvalidTypes") ? Arrays.asList(sa.getParam("InvalidTypes").split(",")) : new ArrayList<>();
int amount = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("Amount", "1"), sa);
final List<String> validTypes = new ArrayList<>();
final List<String> validTypes = Lists.newArrayList();
if (sa.hasParam("ValidTypes")) {
validTypes.addAll(Arrays.asList(sa.getParam("ValidTypes").split(",")));
}
if (validTypes.isEmpty()) {
} else {
switch (type) {
case "Card":
validTypes.addAll(CardType.getAllCardTypes());
@@ -54,22 +53,22 @@ public class ChooseTypeEffect extends SpellAbilityEffect {
}
}
for (final String s : invalidTypes) {
validTypes.remove(s);
if (sa.hasParam("InvalidTypes")) {
for (final String s : sa.getParam("InvalidTypes").split(",")) {
validTypes.remove(s);
}
}
final TargetRestrictions tgt = sa.getTargetRestrictions();
final List<Player> tgtPlayers = getTargetPlayers(sa);
if (!validTypes.isEmpty()) {
for (final Player p : tgtPlayers) {
if ((tgt == null) || p.canBeTargetedBy(sa)) {
String choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes);
if (!sa.hasParam("Secondary")) {
card.setChosenType(choice);
} else {
card.setChosenType2(choice);
for (final Player p : getTargetPlayers(sa)) {
if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) {
List<String> choices = Lists.newArrayList();
for (int i = 0; i < amount; i++) {
// the only one with multiple amount currently cares about the order
choices.addAll(p.getController().chooseSomeType(type, sa, 1, 1, validTypes));
validTypes.removeAll(choices);
}
card.setChosenType(choices, sa);
}
}
}

View File

@@ -49,11 +49,10 @@ public class CleanUpEffect extends SpellAbilityEffect {
source.setChosenPlayer(null);
}
if (sa.hasParam("ClearChosenType")) {
source.setChosenType("");
source.setChosenType2("");
source.setChosenType(null, sa);;
}
if (sa.hasParam("ClearChosenColor")) {
source.setChosenColors(null);
source.setChosenColors(null, sa);
}
}
}

View File

@@ -12,6 +12,7 @@ import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardFactoryUtil;
import forge.game.player.PlayerController.BinaryChoiceType;
import forge.game.replacement.ReplacementResult;
import forge.game.replacement.ReplacementType;
import forge.game.spellability.SpellAbility;
@@ -158,7 +159,7 @@ public class CounterEffect extends SpellAbilityEffect {
* <p>
* removeFromStack.
* </p>
*
*
* @param tgtSA
* a {@link forge.game.spellability.SpellAbility} object.
* @param srcSA
@@ -170,7 +171,7 @@ public class CounterEffect extends SpellAbilityEffect {
private static void removeFromStack(final SpellAbility tgtSA,
final SpellAbility srcSA, final SpellAbilityStackInstance si) {
final Game game = tgtSA.getActivatingPlayer().getGame();
// Run any applicable replacement effects.
// Run any applicable replacement effects.
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(tgtSA.getHostCard());
repParams.put(AbilityKey.TgtSA, tgtSA);
repParams.put(AbilityKey.Cause, srcSA.getHostCard());
@@ -191,7 +192,10 @@ public class CounterEffect extends SpellAbilityEffect {
String destination = srcSA.hasParam("Destination") ? srcSA.getParam("Destination") : tgtSA.isAftermath() ? "Exile" : "Graveyard";
if (srcSA.hasParam("DestinationChoice")) {//Hinder
List<String> pos = Arrays.asList(srcSA.getParam("DestinationChoice").split(","));
destination = srcSA.getActivatingPlayer().getController().chooseSomeType(Localizer.getInstance().getMessage("lblRemoveDestination"), tgtSA, pos, null);
boolean value = srcSA.getActivatingPlayer().getController().chooseBinary(srcSA, Localizer.getInstance().getMessage("lblRemoveDestination"), BinaryChoiceType.BottomOfLibraryOrTopOfLibrary);
destination = pos.get(value ? 1 : 0);
}
if (tgtSA.isAbility()) {
// For Ability-targeted counterspells - do not move it anywhere,
@@ -229,7 +233,6 @@ public class CounterEffect extends SpellAbilityEffect {
runParams.put(AbilityKey.Cause, srcSA.getHostCard());
runParams.put(AbilityKey.CounteredSA, tgtSA);
game.getTriggerHandler().runTrigger(TriggerType.Countered, runParams, false);
if (!tgtSA.isAbility()) {
game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, "Send countered spell to " + destination);

View File

@@ -31,6 +31,8 @@ import forge.util.Localizer;
import forge.util.TextUtil;
import forge.util.collect.FCollectionView;
import org.apache.commons.lang3.StringUtils;
public class DigEffect extends SpellAbilityEffect {
@Override
@@ -187,7 +189,7 @@ public class DigEffect extends SpellAbilityEffect {
}
else if (!changeValid.isEmpty()) {
if (changeValid.contains("ChosenType")) {
changeValid = changeValid.replace("ChosenType", host.getChosenType());
changeValid = changeValid.replace("ChosenType", StringUtils.join(sa.getChosenType(),","));
}
valid = CardLists.getValidCards(top, changeValid.split(","), host.getController(), host, sa);
}
@@ -296,10 +298,6 @@ public class DigEffect extends SpellAbilityEffect {
}
Collections.reverse(movedCards);
Card effectHost = sa.getOriginalHost();
if (effectHost == null) {
effectHost = sa.getHostCard();
}
for (Card c : movedCards) {
final ZoneType origin = c.getZone().getZoneType();
final PlayerZone zone = c.getOwner().getZone(destZone1);
@@ -324,8 +322,9 @@ public class DigEffect extends SpellAbilityEffect {
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")),
1, player, true, counterTable);
}
c.setExiledWith(effectHost);
c.setExiledBy(effectHost.getController());
c.setExiledWith(host);
host.addExiledWith(c, sa);
c.setExiledBy(sa.getActivatingPlayer());
}
}
if (!origin.equals(c.getZone().getZoneType())) {
@@ -335,6 +334,11 @@ public class DigEffect extends SpellAbilityEffect {
if (sa.hasParam("ExileFaceDown")) {
c.turnFaceDown(true);
}
if (sa.hasParam("ExilePeek")) {
c.addMayLookTemp(player);
}
if (sa.hasParam("Imprint")) {
host.addImprintedCard(c);
}
@@ -393,8 +397,9 @@ public class DigEffect extends SpellAbilityEffect {
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")),
1, player, true, counterTable);
}
c.setExiledWith(effectHost);
c.setExiledBy(effectHost.getController());
c.setExiledWith(host);
host.addExiledWith(c, sa);
c.setExiledBy(host.getController());
}
}
}

View File

@@ -232,8 +232,8 @@ public class EffectEffect extends SpellAbilityEffect {
}
// Set Chosen Color(s)
if (hostCard.hasChosenColor()) {
eff.setChosenColors(Lists.newArrayList(hostCard.getChosenColors()));
if (sa.hasChosenColor()) {
eff.setChosenColors(Lists.newArrayList(sa.getChosenColors()), sa);
}
// Set Chosen Cards
@@ -247,8 +247,8 @@ public class EffectEffect extends SpellAbilityEffect {
}
// Set Chosen Type
if (!hostCard.getChosenType().isEmpty()) {
eff.setChosenType(hostCard.getChosenType());
if (sa.hasChosenType()) {
eff.setChosenType(Lists.newArrayList(hostCard.getChosenType(sa)), sa);
}
// Set Chosen name

View File

@@ -56,12 +56,10 @@ public class MillEffect extends SpellAbilityEffect {
p.getGame().getGameLog().add(GameLogEntryType.ZONE_CHANGE, sb.toString());
}
if (destination.equals(ZoneType.Exile)) {
Card host = sa.getOriginalHost();
if (host == null) {
host = sa.getHostCard();
}
for (final Card c : milled) {
c.setExiledWith(host);
c.setExiledWith(source);
source.addExiledWith(source, sa);
c.setExiledBy(sa.getActivatingPlayer());
if (facedown) {
c.turnFaceDown(true);
}

View File

@@ -70,11 +70,11 @@ public class PlayEffect extends SpellAbilityEffect {
amount = AbilityUtils.calculateAmount(source, sa.getParam("Amount"), sa);
}
Player controller = activator;
if (sa.hasParam("Controller")) {
activator = AbilityUtils.getDefinedPlayers(source, sa.getParam("Controller"), sa).get(0);
controller = AbilityUtils.getDefinedPlayers(source, sa.getParam("Controller"), sa).get(0);
}
final Player controller = activator;
CardCollection tgtCards;
CardCollection showCards = new CardCollection();
@@ -118,7 +118,7 @@ public class PlayEffect extends SpellAbilityEffect {
if (sa.hasParam("ChoiceNum")) {
final int choicenum = AbilityUtils.calculateAmount(source, sa.getParam("ChoiceNum"), sa);
tgtCards = new CardCollection(
activator.getController().chooseCardsForEffect(choice, sa,
controller.getController().chooseCardsForEffect(choice, sa,
source + " - " + Localizer.getInstance().getMessage("lblChooseUpTo") + " " + Lang.nounWithNumeral(choicenum, "card"), 0, choicenum, true, null
)
);
@@ -166,9 +166,9 @@ public class PlayEffect extends SpellAbilityEffect {
final CardCollection saidNoTo = new CardCollection();
while (tgtCards.size() > saidNoTo.size() && saidNoTo.size() < amount && amount > 0) {
activator.getController().tempShowCards(showCards);
controller.getController().tempShowCards(showCards);
Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"), null);
activator.getController().endTempShowCards();
controller.getController().endTempShowCards();
if (tgtCard == null) {
return;
}

View File

@@ -60,7 +60,7 @@ public class ProtectAllEffect extends SpellAbilityEffect {
game.getAction().notifyOfValue(sa, choser, Lang.joinHomogenous(gains), choser);
} else {
if (sa.getParam("Gains").equals("ChosenColor")) {
for (final String color : host.getChosenColors()) {
for (final String color : sa.getChosenColors()) {
gains.add(color.toLowerCase());
}
} else if (sa.getParam("Gains").equals("TargetedCardColor")) {

View File

@@ -97,7 +97,6 @@ public class ProtectEffect extends SpellAbilityEffect {
@Override
public void resolve(SpellAbility sa) {
final Card host = sa.getHostCard();
final Game game = sa.getActivatingPlayer().getGame();
final boolean isChoice = sa.getParam("Gains").contains("Choice");
@@ -117,7 +116,7 @@ public class ProtectEffect extends SpellAbilityEffect {
game.getAction().notifyOfValue(sa, choser, Lang.joinHomogenous(gains), choser);
} else {
if (sa.getParam("Gains").equals("ChosenColor")) {
for (final String color : host.getChosenColors()) {
for (final String color : sa.getChosenColors()) {
gains.add(color.toLowerCase());
}
} else {

View File

@@ -3,6 +3,8 @@ package forge.game.ability.effects;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
@@ -289,7 +291,7 @@ public class PumpEffect extends SpellAbilityEffect {
String defined = sa.getParam("DefinedKW");
String replaced = "";
if (defined.equals("ChosenType")) {
replaced = host.getChosenType();
replaced = ObjectUtils.defaultIfNull(sa.getChosenType(0), "");
} else if (defined.equals("CardUIDSource")) {
replaced = "CardUID_" + host.getId();
} else if (defined.equals("ActivatorName")) {

View File

@@ -62,8 +62,8 @@ public class ReplaceManaEffect extends SpellAbilityEffect {
// replace color
String color = sa.getParam("ReplaceColor");
if ("Chosen".equals(color)) {
if (card.hasChosenColor()) {
color = MagicColor.toShortString(card.getChosenColor());
if (sa.hasChosenColor()) {
color = MagicColor.toShortString(sa.getChosenColor());
}
}
if (sa.hasParam("ReplaceOnly")) {

View File

@@ -81,7 +81,7 @@ public class SubgameEffect extends SpellAbilityEffect {
List<String> chosenColors;
SpellAbility cmdColorsa = new SpellAbility.EmptySa(ApiType.ChooseColor, cmd, player);
chosenColors = player.getController().chooseColors(prompt,cmdColorsa, 1, 1, colorChoices);
cmd.setChosenColors(chosenColors);
cmd.setChosenColors(chosenColors, cmdColorsa);
subgame.getAction().notifyOfValue(cmdColorsa, cmd, Localizer.getInstance().getMessage("lblPlayerPickedChosen", player.getName(), Lang.joinHomogenous(chosenColors)), player);
}
cmd.setCommander(true);

View File

@@ -57,12 +57,12 @@ public class TokenEffect extends TokenEffectBase {
// linked Abilities, if it needs chosen values, but nothing is chosen, no token can be created
if (sa.hasParam("TokenTypes")) {
if (sa.getParam("TokenTypes").contains("ChosenType") && !host.hasChosenType()) {
if (sa.getParam("TokenTypes").contains("ChosenType") && !sa.hasChosenType()) {
return;
}
}
if (sa.hasParam("TokenColors")) {
if (sa.getParam("TokenColors").contains("ChosenColor") && !host.hasChosenColor()) {
if (sa.getParam("TokenColors").contains("ChosenColor") && !sa.hasChosenColor()) {
return;
}
}

View File

@@ -0,0 +1,53 @@
package forge.game.card;
import org.apache.commons.lang3.ObjectUtils;
import com.google.common.base.Optional;
import com.google.common.collect.ForwardingTable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
public class ActivationTable extends ForwardingTable<SpellAbility, Optional<StaticAbility>, Integer> {
Table<SpellAbility, Optional<StaticAbility>, Integer> dataTable = HashBasedTable.create();
@Override
protected Table<SpellAbility, Optional<StaticAbility>, Integer> delegate() {
return dataTable;
}
protected SpellAbility getOriginal(SpellAbility sa) {
SpellAbility original = null;
SpellAbility root = sa.getRootAbility();
// because trigger spell abilities are copied, try to get original one
if (root.isTrigger()) {
original = root.getTrigger().getOverridingAbility();
} else {
original = ObjectUtils.defaultIfNull(root.getOriginalAbility(), sa);
}
return original;
}
public void add(SpellAbility sa) {
SpellAbility root = sa.getRootAbility();
SpellAbility original = getOriginal(sa);
Optional<StaticAbility> st = Optional.fromNullable(root.getGrantorStatic());
delegate().put(original, st, ObjectUtils.defaultIfNull(get(original, st), 0) + 1);
}
public Integer get(SpellAbility sa) {
SpellAbility root = sa.getRootAbility();
SpellAbility original = getOriginal(sa);
Optional<StaticAbility> st = Optional.fromNullable(root.getGrantorStatic());
if (contains(original, st)) {
return get(original, st);
}
return 0;
}
}

View File

@@ -33,6 +33,7 @@ import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.tuple.Pair;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.HashBasedTable;
@@ -294,9 +295,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private NavigableMap<Long, Player> tempControllers = Maps.newTreeMap();
private String originalText = "", text = "";
private String chosenType = "";
private String chosenType2 = "";
private List<String> chosenColors;
private LinkedAbilityTable<String> chosenTypesTable = new LinkedAbilityTable<String>();
private LinkedAbilityTable<String> chosenColorsTable = new LinkedAbilityTable<String>();
private String chosenName = "";
private String chosenName2 = "";
private Integer chosenNumber;
@@ -307,6 +307,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private Card exiledWith = null;
private Player exiledBy = null;
private ExileWithTable exiledWithTable = new ExileWithTable();
private Map<Long, Player> goad = Maps.newTreeMap();
@@ -338,17 +339,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private int planeswalkerAbilityActivated = 0;
private final Map<SpellAbility, Integer> numberTurnActivations = Maps.newHashMap();
private final Map<SpellAbility, Integer> numberGameActivations = Maps.newHashMap();
private final ActivationTable numberTurnActivations = new ActivationTable();
private final ActivationTable numberGameActivations = new ActivationTable();
private final Table<SpellAbility, StaticAbility, Integer> numberTurnActivationsStatic = HashBasedTable.create();
private final Table<SpellAbility, StaticAbility, Integer> numberGameActivationsStatic = HashBasedTable.create();
private final Map<SpellAbility, List<String>> chosenModesTurn = Maps.newHashMap();
private final Map<SpellAbility, List<String>> chosenModesGame = Maps.newHashMap();
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesTurnStatic = HashBasedTable.create();
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesGameStatic = HashBasedTable.create();
private final ChosenModesTable chosenModesTurn = new ChosenModesTable();
private final ChosenModesTable chosenModesGame = new ChosenModesTable();
// Enumeration for CMC request types
public enum SplitCMCMode {
@@ -1686,55 +1681,39 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
exiledBy = ep;
}
// used for cards like Belbe's Portal, Conspiracy, Cover of Darkness, etc.
public final String getChosenType() {
return chosenType;
public final Iterable<String> getChosenType(CardTraitBase ctb) {
return chosenTypesTable.get(ctb);
}
public final void setChosenType(final String s) {
chosenType = s;
view.updateChosenType(this);
public final String getChosenType(CardTraitBase ctb, int index) {
return Iterables.get(chosenTypesTable.get(ctb), index, null);
}
public final boolean hasChosenType() {
return chosenType != null && !chosenType.isEmpty();
public final boolean hasChosenType(CardTraitBase ctb) {
return !chosenTypesTable.get(ctb).isEmpty();
}
// used by card Illusionary Terrain
public final String getChosenType2() {
return chosenType2;
public void setChosenType(final Iterable<String> types, CardTraitBase ctb) {
chosenTypesTable.set(types, ctb);
}
public final void setChosenType2(final String s) {
chosenType2 = s;
view.updateChosenType2(this);
public String getChosenColor(CardTraitBase ctb) {
return Iterables.getFirst(chosenColorsTable.get(ctb), null);
}
public final Iterable<String> getChosenColors(CardTraitBase ctb) {
return chosenColorsTable.get(ctb);
}
public final boolean hasChosenType2() {
return chosenType2 != null && !chosenType2.isEmpty();
public final boolean hasChosenColor(CardTraitBase ctb) {
return !chosenColorsTable.get(ctb).isEmpty();
}
public final String getChosenColor() {
if (hasChosenColor()) {
return chosenColors.get(0);
}
return "";
public final boolean hasChosenColor(String s, CardTraitBase ctb) {
return chosenColorsTable.contains(s, ctb);
}
public final Iterable<String> getChosenColors() {
if (chosenColors == null) {
return Lists.newArrayList();
}
return chosenColors;
}
public final void setChosenColors(final List<String> s) {
chosenColors = s;
view.updateChosenColors(this);
}
public boolean hasChosenColor() {
return chosenColors != null && !chosenColors.isEmpty();
}
public boolean hasChosenColor(String s) {
return chosenColors != null && chosenColors.contains(s);
public void setChosenColors(final Iterable<String> colors, CardTraitBase ctb) {
chosenColorsTable.set(colors, ctb);
}
public final Card getChosenCard() {
@@ -6858,20 +6837,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
}
public void addAbilityActivated(SpellAbility ability) {
SpellAbility original = ability.getOriginalAbility();
if (original == null) {
original = ability;
}
int turnActivated = getAbilityActivatedThisTurn(ability);
int gameActivated = getAbilityActivatedThisGame(ability);
if (ability.getGrantorStatic() != null) {
numberTurnActivationsStatic.put(original, ability.getGrantorStatic(), turnActivated + 1);
numberGameActivationsStatic.put(original, ability.getGrantorStatic(), gameActivated + 1);
} else {
numberTurnActivations.put(original, turnActivated + 1);
numberGameActivations.put(original, gameActivated + 1);
}
numberTurnActivations.add(ability);
numberGameActivations.add(ability);
if (ability.isPwAbility()) {
addPlaneswalkerAbilityActivated();
@@ -6879,121 +6846,27 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
}
public int getAbilityActivatedThisTurn(SpellAbility ability) {
SpellAbility original = ability.getOriginalAbility();
if (original == null) {
original = ability;
}
if (ability.getGrantorStatic() != null) {
if (numberTurnActivationsStatic.contains(original, ability.getGrantorStatic())) {
return numberTurnActivationsStatic.get(original, ability.getGrantorStatic());
}
return 0;
}
return numberTurnActivations.containsKey(original) ? numberTurnActivations.get(original) : 0;
return numberTurnActivations.get(ability);
}
public int getAbilityActivatedThisGame(SpellAbility ability) {
SpellAbility original = ability.getOriginalAbility();
if (original == null) {
original = ability;
}
if (ability.getGrantorStatic() != null) {
if (numberGameActivationsStatic.contains(original, ability.getGrantorStatic())) {
return numberGameActivationsStatic.get(original, ability.getGrantorStatic());
}
return 0;
}
return numberGameActivations.containsKey(original) ? numberGameActivations.get(original) : 0;
return numberGameActivations.get(ability);
}
public List<String> getChosenModesTurn(SpellAbility ability) {
SpellAbility original = null;
SpellAbility root = ability.getRootAbility();
// because trigger spell abilities are copied, try to get original one
if (root.isTrigger()) {
original = root.getTrigger().getOverridingAbility();
} else {
original = ability.getOriginalAbility();
if (original == null) {
original = ability;
}
}
if (ability.getGrantorStatic() != null) {
return chosenModesTurnStatic.get(original, ability.getGrantorStatic());
}
return chosenModesTurn.get(original);
return chosenModesTurn.get(ability);
}
public List<String> getChosenModesGame(SpellAbility ability) {
SpellAbility original = null;
SpellAbility root = ability.getRootAbility();
// because trigger spell abilities are copied, try to get original one
if (root.isTrigger()) {
original = root.getTrigger().getOverridingAbility();
} else {
original = ability.getOriginalAbility();
if (original == null) {
original = ability;
}
}
if (ability.getGrantorStatic() != null) {
return chosenModesGameStatic.get(original, ability.getGrantorStatic());
}
return chosenModesGame.get(original);
return chosenModesGame.get(ability);
}
public void addChosenModes(SpellAbility ability, String mode) {
SpellAbility original = null;
SpellAbility root = ability.getRootAbility();
// because trigger spell abilities are copied, try to get original one
if (root.isTrigger()) {
original = root.getTrigger().getOverridingAbility();
} else {
original = ability.getOriginalAbility();
if (original == null) {
original = ability;
}
}
if (ability.getGrantorStatic() != null) {
List<String> result = chosenModesTurnStatic.get(original, ability.getGrantorStatic());
if (result == null) {
result = Lists.newArrayList();
chosenModesTurnStatic.put(original, ability.getGrantorStatic(), result);
}
result.add(mode);
result = chosenModesGameStatic.get(original, ability.getGrantorStatic());
if (result == null) {
result = Lists.newArrayList();
chosenModesGameStatic.put(original, ability.getGrantorStatic(), result);
}
result.add(mode);
} else {
List<String> result = chosenModesTurn.get(original);
if (result == null) {
result = Lists.newArrayList();
chosenModesTurn.put(original, result);
}
result.add(mode);
result = chosenModesGame.get(original);
if (result == null) {
result = Lists.newArrayList();
chosenModesGame.put(original, result);
}
result.add(mode);
}
chosenModesTurn.put(ability, mode);
chosenModesGame.put(ability, mode);
}
public void resetChosenModeTurn() {
chosenModesTurn.clear();
chosenModesTurnStatic.clear();
}
public int getPlaneswalkerAbilityActivated() {
@@ -7007,7 +6880,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
public void resetActivationsPerTurn() {
planeswalkerAbilityActivated = 0;
numberTurnActivations.clear();
numberTurnActivationsStatic.clear();
}
public void addCanBlockAdditional(int n, long timestamp) {
@@ -7077,6 +6949,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return edition.getBorderColor();
}
public final void addUntilLeavesBattlefield(final Card c) {
untilLeavesBattlefield = view.addCard(untilLeavesBattlefield, c, TrackableProperty.UntilLeavesBattlefield);
}
@@ -7092,4 +6965,32 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
public final void clearUntilLeavesBattlefield() {
untilLeavesBattlefield = view.clearCards(untilLeavesBattlefield, TrackableProperty.UntilLeavesBattlefield);
}
public void addExiledWith(Card object) {
this.exiledWithTable.put(object, this);
}
public void addExiledWith(Card object, CardTraitBase ctb) {
this.exiledWithTable.put(object, ctb);
}
public boolean removeExiledWith(Card object) {
return exiledWithTable.remove(object);
}
public ExileWithTable getExiledWithTable() {
return exiledWithTable;
}
public void setExiledWithTable(Table<Card, Optional<StaticAbility>, FCollection<Card>> map) {
exiledWithTable = new ExileWithTable(map);
}
public FCollection<Card> getExiledWith(CardTraitBase ctb) {
return exiledWithTable.get(ctb);
}
public boolean isExiledWith(Card object, CardTraitBase ctb) {
return exiledWithTable.contains(object, ctb);
}
}

View File

@@ -129,7 +129,6 @@ public class CardFactory {
* </p>
* */
private final static Card copySpellHost(final SpellAbility sourceSA, final SpellAbility targetSA, Player controller) {
final Card source = sourceSA.getHostCard();
final Card original = targetSA.getHostCard();
final Card c = copyCard(original, true);
@@ -138,7 +137,7 @@ public class CardFactory {
String tmp = "";
final String newColor = sourceSA.getParam("CopyIsColor");
if (newColor.equals("ChosenColor")) {
tmp = CardUtil.getShortColorsString(source.getChosenColors());
tmp = CardUtil.getShortColorsString(sourceSA.getChosenColors());
} else {
tmp = CardUtil.getShortColorsString(Lists.newArrayList(newColor.split(",")));
}

View File

@@ -1052,28 +1052,6 @@ public class CardFactoryUtil {
}
return doXMath(colorOcurrencices, m, c);
}
// Count$DevotionDual.<color name>.<color name>
// Count$Devotion.<color name>
if (sq[0].contains("Devotion")) {
int colorOcurrencices = 0;
String colorName = sq[1];
if (colorName.contains("Chosen")) {
colorName = MagicColor.toShortString(c.getChosenColor());
}
byte colorCode = ManaAtom.fromName(colorName);
if (sq[0].equals("DevotionDual")) {
colorCode |= ManaAtom.fromName(sq[2]);
}
for (Card c0 : cc.getCardsIn(ZoneType.Battlefield)) {
for (ManaCostShard sh : c0.getManaCost()) {
if ((sh.getColorMask() & colorCode) != 0) {
colorOcurrencices++;
}
}
colorOcurrencices += c0.getAmountOfKeyword("Your devotion to each color and each combination of colors is increased by one.");
}
return doXMath(colorOcurrencices, m, c);
}
if (sq[0].contains("ColorsCtrl")) {
final String restriction = l[0].substring(11);
@@ -2751,8 +2729,8 @@ public class CardFactoryUtil {
sb.append(", then put the rest on the bottom of your library.");
final Trigger hideawayTrigger = TriggerHandler.parseTrigger(sb.toString(), card, intrinsic);
String hideawayDig = "DB$ Dig | Defined$ You | DigNum$ 4 | DestinationZone$ Exile | ExileFaceDown$ True | RememberChanged$ True";
String hideawayEffect = "DB$ Effect | StaticAbilities$ STHideawayEffectLookAtCard | ForgetOnMoved$ Exile | RememberObjects$ Remembered | Duration$ Permanent";
String hideawayDig = "DB$ Dig | Defined$ You | DigNum$ 4 | DestinationZone$ Exile | ExileFaceDown$ True";
String hideawayEffect = "DB$ Effect | StaticAbilities$ STHideawayEffectLookAtCard | ForgetOnMoved$ Exile | RememberObjects$ ExiledWith | Duration$ Permanent";
String lookAtCard = "Mode$ Continuous | Affected$ Card.IsRemembered | MayLookAt$ True | EffectZone$ Command | AffectedZone$ Exile | Description$ You may look at the exiled card.";
@@ -2771,12 +2749,6 @@ public class CardFactoryUtil {
gainControlTrigger.setOverridingAbility((AbilitySub)effectSA.copy());
triggers.add(gainControlTrigger);
// when the card with hideaway leaves the battlefield, forget all exiled cards
final Trigger changeZoneTrigger = TriggerHandler.parseTrigger("Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | TriggerZone$ Battlefield | Static$ True", card, intrinsic);
String cleanupStr = "DB$ Cleanup | ClearRemembered$ True";
changeZoneTrigger.setOverridingAbility(AbilityFactory.getAbility(cleanupStr, card));
triggers.add(changeZoneTrigger);
for (final Trigger trigger : triggers) {
inst.addTrigger(trigger);
}

View File

@@ -378,23 +378,23 @@ public class CardProperty {
return false;
}
Card host = source;
//Static Abilites doesn't have spellAbility or OriginalHost
if (spellAbility != null) {
host = spellAbility.getOriginalHost();
if (host == null) {
host = spellAbility.getHostCard();
}
if (!source.isExiledWith(card, spellAbility)) {
return false;
}
if (!card.getExiledWith().equals(host)) {
if (!card.getExiledWith().equals(source)) {
return false;
}
} else if (property.equals("ExiledWithEffectSource")) {
if (card.getExiledWith() == null) {
return false;
}
if (!card.getExiledWith().equals(source.getEffectSource())) {
if (!source.isEmblem() && !source.getType().hasSubtype("Effect")) {
return false;
}
if (!source.getEffectSource().isExiledWith(card, spellAbility)) {
return false;
}
} else if (property.equals("EncodedWithSource")) {
@@ -895,23 +895,22 @@ public class CardProperty {
return Iterables.any(game.getCardsIn(ZoneType.Battlefield), CardPredicates.sharesNameWith(card));
} else if (restriction.equals("ThisTurnCast")) {
return Iterables.any(CardUtil.getThisTurnCast("Card", source), CardPredicates.sharesNameWith(card));
} else if (restriction.equals("MovedToGrave")) {
for (final SpellAbility sa : source.getCurrentState().getNonManaAbilities()) {
final SpellAbility root = sa.getRootAbility();
if (root != null && (root.getPaidList("MovedToGrave") != null)
&& !root.getPaidList("MovedToGrave").isEmpty()) {
final CardCollectionView cards = root.getPaidList("MovedToGrave");
for (final Card c : cards) {
String name = c.getName();
if (StringUtils.isEmpty(name)) {
name = c.getPaperCard().getName();
}
if (card.getName().equals(name)) {
return true;
}
} else if (restriction.equals("MovedToGrave") && spellAbility instanceof SpellAbility) {
final SpellAbility root = ((SpellAbility)spellAbility).getRootAbility();
if (root != null && (root.getPaidList("MovedToGrave") != null)
&& !root.getPaidList("MovedToGrave").isEmpty()) {
final CardCollectionView cards = root.getPaidList("MovedToGrave");
for (final Card c : cards) {
String name = c.getName();
if (StringUtils.isEmpty(name)) {
name = c.getPaperCard().getName();
}
if (card.getName().equals(name)) {
return true;
}
}
}
return false;
} else if (restriction.equals("NonToken")) {
return !CardLists.filter(game.getCardsIn(ZoneType.Battlefield),
@@ -966,7 +965,7 @@ public class CardProperty {
final String restriction = property.split("sharesControllerWith ")[1];
if (restriction.startsWith("Remembered") || restriction.startsWith("Imprinted")) {
CardCollection list = AbilityUtils.getDefinedCards(source, restriction, spellAbility);
return !CardLists.filter(list, CardPredicates.sharesControllerWith(card)).isEmpty();
return Iterables.any(list, CardPredicates.sharesControllerWith(card));
}
}
} else if (property.startsWith("sharesOwnerWith")) {

View File

@@ -317,6 +317,9 @@ public final class CardUtil {
newCopy.setCastFrom(in.getCastFrom());
newCopy.setExiledWith(getLKICopy(in.getExiledWith(), cachedMap));
newCopy.setExiledBy(in.getExiledBy());
newCopy.setExiledWithTable(in.getExiledWithTable());
return newCopy;
}

View File

@@ -2,7 +2,6 @@ package forge.game.card;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -319,20 +318,6 @@ public class CardView extends GameEntityView {
set(TrackableProperty.ShieldCount, c.getShieldCount());
}
public String getChosenType() {
return get(TrackableProperty.ChosenType);
}
void updateChosenType(Card c) {
set(TrackableProperty.ChosenType, c.getChosenType());
}
public String getChosenType2() {
return get(TrackableProperty.ChosenType2);
}
void updateChosenType2(Card c) {
set(TrackableProperty.ChosenType2, c.getChosenType2());
}
public String getChosenNumber() {
return get(TrackableProperty.ChosenNumber);
}
@@ -340,13 +325,6 @@ public class CardView extends GameEntityView {
set(TrackableProperty.ChosenNumber, c.getChosenNumber().toString());
}
public List<String> getChosenColors() {
return get(TrackableProperty.ChosenColors);
}
void updateChosenColors(Card c) {
set(TrackableProperty.ChosenColors, c.getChosenColors());
}
public FCollectionView<CardView> getMergedCardsCollection() {
return get(TrackableProperty.MergedCardsCollection);
}

View File

@@ -0,0 +1,64 @@
package forge.game.card;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils;
import com.google.common.base.Optional;
import com.google.common.collect.ForwardingTable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
public class ChosenModesTable extends ForwardingTable<SpellAbility, Optional<StaticAbility>, List<String>> {
Table<SpellAbility, Optional<StaticAbility>, List<String>> dataTable = HashBasedTable.create();
@Override
protected Table<SpellAbility, Optional<StaticAbility>, List<String>> delegate() {
return dataTable;
}
protected SpellAbility getOriginal(SpellAbility sa) {
SpellAbility original = null;
SpellAbility root = sa.getRootAbility();
// because trigger spell abilities are copied, try to get original one
if (root.isTrigger()) {
original = root.getTrigger().getOverridingAbility();
} else {
original = ObjectUtils.defaultIfNull(root.getOriginalAbility(), sa);
}
return original;
}
public List<String> put(SpellAbility sa, String mode) {
SpellAbility root = sa.getRootAbility();
SpellAbility original = getOriginal(sa);
Optional<StaticAbility> st = Optional.fromNullable(root.getGrantorStatic());
List<String> old;
if (contains(original, st)) {
old = get(original, st);
old.add(mode);
} else {
old = Lists.newArrayList(mode);
delegate().put(original, st, old);
}
return old;
}
public List<String> get(SpellAbility sa) {
SpellAbility root = sa.getRootAbility();
SpellAbility original = getOriginal(sa);
Optional<StaticAbility> st = Optional.fromNullable(root.getGrantorStatic());
if (contains(original, st)) {
return get(original, st);
} else {
return ImmutableList.of();
}
}
}

View File

@@ -0,0 +1,18 @@
package forge.game.card;
import com.google.common.base.Optional;
import com.google.common.collect.Table;
import forge.game.staticability.StaticAbility;
import forge.util.collect.FCollection;
public class ExileWithTable extends LinkedAbilityTable<Card> {
public ExileWithTable(Table<Card, Optional<StaticAbility>, FCollection<Card>> map) {
this.putAll(map);
}
public ExileWithTable() {
}
}

View File

@@ -0,0 +1,89 @@
package forge.game.card;
import org.apache.commons.lang3.ObjectUtils;
import com.google.common.base.Optional;
import com.google.common.collect.ForwardingTable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Table;
import forge.game.CardTraitBase;
import forge.game.staticability.StaticAbility;
import forge.util.collect.FCollection;
public class LinkedAbilityTable<T> extends ForwardingTable<Card, Optional<StaticAbility>, FCollection<T>> {
private Table<Card, Optional<StaticAbility>, FCollection<T>> dataTable = HashBasedTable.create();
@Override
protected Table<Card, Optional<StaticAbility>, FCollection<T>> delegate() {
return dataTable;
}
protected FCollection<T> getSupplier() {
return new FCollection<T>();
}
protected FCollection<T> putInternal(T object, Card host, StaticAbility stAb) {
host = ObjectUtils.defaultIfNull(host.getEffectSource(), host);
Optional<StaticAbility> st = Optional.fromNullable(stAb);
FCollection<T> old;
if (contains(host, st)) {
old = get(host, st);
} else {
old = getSupplier();
delegate().put(host, st, old);
}
old.add(object);
return old;
}
public FCollection<T> put(T object, Card host) {
return putInternal(object, host, null);
}
public FCollection<T> put(T object, CardTraitBase ctb) {
return putInternal(object, ctb.getOriginalOrHost(), ctb.getGrantorStatic());
}
protected void setInternal(Iterable<T> list, Card host, StaticAbility stAb) {
host = ObjectUtils.defaultIfNull(host.getEffectSource(), host);
Optional<StaticAbility> st = Optional.fromNullable(stAb);
if (list == null || Iterables.isEmpty(list)) {
delegate().remove(host, st);
} else {
FCollection<T> old = getSupplier();
old.addAll(list);
delegate().put(host, st, old);
}
}
public void set(Iterable<T> list, CardTraitBase ctb) {
setInternal(list, ctb.getOriginalOrHost(), ctb.getGrantorStatic());
}
public FCollection<T> get(CardTraitBase ctb) {
Card host = ctb.getOriginalOrHost();
host = ObjectUtils.defaultIfNull(host.getEffectSource(), host);
Optional<StaticAbility> st = Optional.fromNullable(ctb.getGrantorStatic());
if (contains(host, st)) {
return get(host, st);
} else {
return FCollection.<T>getEmpty();
}
}
public boolean contains(T object, CardTraitBase ctb) {
return get(ctb).contains(object);
}
public boolean remove(T value) {
boolean changed = false;
for (FCollection<T> col : delegate().values()) {
if (col.remove(value)) {
changed = true;
}
}
return changed;
}
}

View File

@@ -3,7 +3,6 @@ package forge.game.card.token;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Joiner;
@@ -332,7 +331,7 @@ public class TokenInfo {
final Card host = sa.getHostCard();
final Game game = host.getGame();
String edition = ObjectUtils.firstNonNull(sa.getOriginalHost(), host).getSetCode();
String edition = sa.getOriginalOrHost().getSetCode();
PaperToken token = StaticData.instance().getAllTokens().getToken(script, edition);
if (token == null) {
@@ -359,14 +358,16 @@ public class TokenInfo {
// need to be done after text change so it isn't affected by that
if (sa.hasParam("TokenTypes")) {
String types = sa.getParam("TokenTypes");
types = types.replace("ChosenType", sa.getHostCard().getChosenType());
if (sa.hasChosenType()) {
types = types.replace("ChosenType", Iterables.getFirst(sa.getChosenType(), null));
}
result.addType(types);
result.setName(types);
}
if (sa.hasParam("TokenColors")) {
String colors = sa.getParam("TokenColors");
colors = colors.replace("ChosenColor", sa.getHostCard().getChosenColor());
colors = colors.replace("ChosenColor", sa.getChosenColor());
result.setColor(MagicColor.toShortString(colors));
}

View File

@@ -233,7 +233,8 @@ public class CostAdjustment {
sa.getHostCard().addDelved(c);
final Card d = game.getAction().exile(c, null);
d.setExiledWith(sa.getHostCard());
d.setExiledBy(sa.getHostCard().getController());
sa.getHostCard().addExiledWith(d, sa);
d.setExiledBy(sa.getActivatingPlayer());
table.put(ZoneType.Graveyard, d.getZone().getZoneType(), d);
}
}

View File

@@ -55,7 +55,7 @@ public class CostChooseCreatureType extends CostPart {
@Override
public boolean payAsDecided(Player payer, PaymentDecision pd, SpellAbility sa) {
sa.getHostCard().setChosenType(pd.type);
sa.getHostCard().setChosenType(pd.types, sa);
return true;
}

View File

@@ -12,7 +12,7 @@ import forge.util.TextUtil;
public class PaymentDecision {
public int c = 0;
public String type;
public Iterable<String> types = null;
public final CardCollection cards = new CardCollection();
public final List<Mana> mana;
@@ -43,9 +43,9 @@ public class PaymentDecision {
cards.add(chosen);
}
public PaymentDecision(String choice) {
public PaymentDecision(Iterable<String> choices) {
this(null, null, null, null, null);
type = choice;
types = choices;
}
public static PaymentDecision card(Card chosen) {
@@ -86,8 +86,8 @@ public class PaymentDecision {
return TextUtil.concatWithSpace("Payment Decision:", TextUtil.addSuffix(String.valueOf(c),","), cards.toString());
}
public static PaymentDecision type(String choice) {
return new PaymentDecision(choice);
public static PaymentDecision types(Iterable<String> choices) {
return new PaymentDecision(choices);
}
public static PaymentDecision players(List<Player> players) {

View File

@@ -24,10 +24,9 @@ import java.util.Map.Entry;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import forge.card.CardType;
import forge.game.Game;
import forge.game.ability.ApiType;
import forge.game.card.Card;
@@ -138,10 +137,9 @@ public class Untap extends Phase {
}
if (kw.startsWith("OnlyUntapChosen") && !hasChosen) {
List<String> validTypes = Arrays.asList(kw.split(":")[1].split(","));
List<String> invalidTypes = Lists.newArrayList(CardType.getAllCardTypes());
invalidTypes.removeAll(validTypes);
final String chosen = player.getController().chooseSomeType("Card", new SpellAbility.EmptySa(ApiType.ChooseType, null, player), validTypes, invalidTypes);
list = CardLists.getType(list,chosen);
SpellAbility emptySA = new SpellAbility.EmptySa(ApiType.ChooseType, null, player);
final String chosen = Iterables.getFirst(player.getController().chooseSomeType("Card", emptySA, 1, 1, validTypes), "");
list = CardLists.getType(list, chosen);
hasChosen = true;
}
}

View File

@@ -3039,7 +3039,7 @@ public class Player extends GameEntity implements Comparable<Player> {
List<String> chosenColors;
SpellAbility cmdColorsa = new SpellAbility.EmptySa(ApiType.ChooseColor, cmd, p);
chosenColors = p.getController().chooseColors(prompt,cmdColorsa, 1, 1, colorChoices);
cmd.setChosenColors(chosenColors);
cmd.setChosenColors(chosenColors, cmdColorsa);
p.getGame().getAction().notifyOfValue(cmdColorsa, cmd, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), Lang.joinHomogenous(chosenColors)), p);
}
cmd.setCommander(true);

View File

@@ -48,9 +48,9 @@ import forge.item.PaperCard;
import forge.util.ITriggerEvent;
import forge.util.collect.FCollectionView;
/**
/**
* A prototype for player controller class
*
*
* Handles phase skips for now.
*/
public abstract class PlayerController {
@@ -72,6 +72,7 @@ public abstract class PlayerController {
UntapTimeVault,
LeftOrRight,
AddOrRemove,
BottomOfLibraryOrTopOfLibrary,
}
protected final GameView gameView;
@@ -113,16 +114,16 @@ public abstract class PlayerController {
public abstract CardCollectionView choosePermanentsToSacrifice(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
public abstract CardCollectionView choosePermanentsToDestroy(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
public abstract TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate<GameObject> filter, boolean optional);
public abstract boolean chooseTargetsFor(SpellAbility currentAbility); // this is bad a function for it assigns targets to sa inside its body
public abstract boolean chooseTargetsFor(SpellAbility currentAbility); // this is bad a function for it assigns targets to sa inside its body
// Specify a target of a spell (Spellskite)
public abstract Pair<SpellAbilityStackInstance, GameObject> chooseTarget(SpellAbility sa, List<Pair<SpellAbilityStackInstance, GameObject>> allTargets);
// Q: why is there min/max and optional at once? A: This is to handle cases like 'choose 3 to 5 cards or none at all'
// Q: why is there min/max and optional at once? A: This is to handle cases like 'choose 3 to 5 cards or none at all'
public abstract CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional, Map<String, Object> params);
public final <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, SpellAbility sa, String title, Map<String, Object> params) { return chooseSingleEntityForEffect(optionList, null, sa, title, false, null, params); }
public final <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, SpellAbility sa, String title, boolean isOptional, Map<String, Object> params) { return chooseSingleEntityForEffect(optionList, null, sa, title, isOptional, null, params); }
public final <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, SpellAbility sa, String title, boolean isOptional, Map<String, Object> params) { return chooseSingleEntityForEffect(optionList, null, sa, title, isOptional, null, params); }
public abstract <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player relatedPlayer, Map<String, Object> params);
public abstract List<SpellAbility> chooseSpellAbilitiesForEffect(List<SpellAbility> spells, SpellAbility sa, String title, int num, Map<String, Object> params);
@@ -174,11 +175,6 @@ public abstract class PlayerController {
public abstract List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand);
public abstract Mana chooseManaFromPool(List<Mana> manaChoices);
public abstract String chooseSomeType(String kindOfType, SpellAbility sa, Collection<String> validTypes, List<String> invalidTypes, boolean isOptional);
public final String chooseSomeType(String kindOfType, SpellAbility sa, Collection<String> validTypes, List<String> invalidTypes) {
return chooseSomeType(kindOfType, sa, validTypes, invalidTypes, false);
}
public abstract Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer);
public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question);
@@ -208,7 +204,7 @@ public abstract class PlayerController {
public final boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice) { return chooseBinary(sa, question, kindOfChoice, (Boolean) null); }
public abstract boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Boolean defaultChioce);
public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Map<String, Object> params) { return chooseBinary(sa, question, kindOfChoice); }
public abstract boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call);
public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap);
@@ -220,6 +216,8 @@ public abstract class PlayerController {
public abstract ICardFace chooseSingleCardFace(SpellAbility sa, String message, Predicate<ICardFace> cpp, String name);
public abstract List<String> chooseColors(String message, SpellAbility sa, int min, int max, List<String> options);
public abstract List<String> chooseSomeType(String kindOfType, SpellAbility sa, int min, int max, List<String> validTypes);
public abstract CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, String prompt,
Map<String, Object> params);
@@ -259,7 +257,7 @@ public abstract class PlayerController {
public abstract String chooseCardName(SpellAbility sa, Predicate<ICardFace> cpp, String valid, String message);
public abstract String chooseCardName(SpellAbility sa, List<ICardFace> faces, String message);
// better to have this odd method than those if playerType comparison in ChangeZone
// better to have this odd method than those if playerType comparison in ChangeZone
public abstract Card chooseSingleCardForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, boolean isOptional, Player decider);
public abstract List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, int min, int max, DelayedReveal delayedReveal, String selectPrompt, Player decider);

View File

@@ -71,6 +71,7 @@ public class AbilityManaPart implements java.io.Serializable {
private transient List<Mana> lastManaProduced = Lists.newArrayList();
private transient Card sourceCard;
private transient SpellAbility spellAbility;
// Spells paid with this mana spell can't be countered.
@@ -84,8 +85,9 @@ public class AbilityManaPart implements java.io.Serializable {
* @param sourceCard
* a {@link forge.game.card.Card} object.
*/
public AbilityManaPart(final Card sourceCard, final Map<String, String> params) {
public AbilityManaPart(final Card sourceCard, final SpellAbility sa, final Map<String, String> params) {
this.sourceCard = sourceCard;
this.spellAbility = sa;
origProduced = params.containsKey("Produced") ? params.get("Produced") : "1";
this.manaRestrictions = params.containsKey("RestrictValid") ? params.get("RestrictValid") : "";
@@ -413,9 +415,11 @@ public class AbilityManaPart implements java.io.Serializable {
* @return a {@link java.lang.String} object.
*/
public final String mana() {
if (this.getOrigProduced().contains("Chosen")) {
if (this.getSourceCard() != null && this.getSourceCard().hasChosenColor()) {
return MagicColor.toShortString(this.getSourceCard().getChosenColor());
if (getOrigProduced().contains("Chosen")) {
if (spellAbility == null) {
return "";
} else {
return MagicColor.toShortString(spellAbility.getChosenColor());
}
}
return this.getOrigProduced();
@@ -499,15 +503,15 @@ public class AbilityManaPart implements java.io.Serializable {
* a {@link java.lang.String} object.
* @return a boolean.
*/
public final boolean canProduce(final String s, final SpellAbility sa) {
public final boolean canProduce(final String s) {
// Any mana never means Colorless?
if (isAnyMana() && !s.equals("C")) {
return true;
}
String origProduced = getOrigProduced();
if (origProduced.contains("Chosen") && sourceCard != null ) {
if (getSourceCard().hasChosenColor() && MagicColor.toShortString(getSourceCard().getChosenColor()).contains(s)) {
if (origProduced.contains("Chosen") && spellAbility != null ) {
if (spellAbility.hasChosenColor() && MagicColor.toShortString(spellAbility.getChosenColor()).contains(s)) {
return true;
}
}

View File

@@ -89,7 +89,7 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab
effect = api.getSpellEffect();
if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) {
this.setManaPart(new AbilityManaPart(ca, mapParams));
this.setManaPart(new AbilityManaPart(ca, this, mapParams));
}
if (api.equals(ApiType.ChangeZone) || api.equals(ApiType.ChangeZoneAll)) {

View File

@@ -110,7 +110,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private Player targetingPlayer = null;
private SpellAbility grantorOriginal = null;
private StaticAbility grantorStatic = null;
private CardCollection splicedCards = null;
@@ -253,7 +252,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public boolean canThisProduce(final String s) {
AbilityManaPart mp = getManaPart();
if (mp != null && metConditions() && mp.canProduce(s, this)) {
if (mp != null && metConditions() && mp.canProduce(s)) {
return true;
}
return false;
@@ -994,7 +993,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
clone.setPayCosts(getPayCosts().copy());
if (manaPart != null) {
clone.manaPart = new AbilityManaPart(host, mapParams);
clone.manaPart = new AbilityManaPart(host, clone, mapParams);
}
// need to copy the damage tables
@@ -2203,14 +2202,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
grantorOriginal = sa;
}
public StaticAbility getGrantorStatic() {
return grantorStatic;
}
public void setGrantorStatic(final StaticAbility st) {
grantorStatic = st;
}
public boolean isAlternativeCost(AlternativeCost ac) {
if (ac.equals(altCost)) {
return true;

View File

@@ -339,7 +339,7 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
}
if (this.getColorToCheck() != null) {
if (!sa.getHostCard().hasChosenColor(this.getColorToCheck())) {
if (!sa.hasChosenColor(this.getColorToCheck())) {
return false;
}
}

View File

@@ -362,7 +362,7 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
}
if (getColorToCheck() != null) {
if (!sa.getHostCard().hasChosenColor(getColorToCheck())) {
if (!sa.hasChosenColor(getColorToCheck())) {
return false;
}
}

View File

@@ -168,7 +168,7 @@ public final class StaticAbilityContinuous {
if (layer == StaticAbilityLayer.TEXT && params.containsKey("GainTextOf")) {
final String valid = params.get("GainTextOf");
CardCollection allValid = CardLists.getValidCards(game.getCardsInGame(), valid, hostCard.getController(), hostCard, null);
CardCollection allValid = CardLists.getValidCards(game.getCardsInGame(), valid, hostCard.getController(), hostCard, stAb);
if (allValid.size() > 1) {
// TODO: if ever necessary, support gaining text of multiple cards at the same time
System.err.println("Error: GainTextOf parameter was not defined as a unique card for " + hostCard);
@@ -215,10 +215,10 @@ public final class StaticAbilityContinuous {
Iterables.removeIf(addKeywords, new Predicate<String>() {
@Override
public boolean apply(String input) {
if (!hostCard.hasChosenColor() && input.contains("ChosenColor")) {
if (!stAb.hasChosenColor() && input.contains("ChosenColor")) {
return true;
}
if (!hostCard.hasChosenType() && input.contains("ChosenType")) {
if (!stAb.hasChosenType() && input.contains("ChosenType")) {
return true;
}
if (!hostCard.hasChosenNumber() && input.contains("ChosenNumber")) {
@@ -271,7 +271,7 @@ public final class StaticAbilityContinuous {
String keywordDefined = params.get("KeywordDefined");
CardCollectionView definedCards = game.getCardsIn(ZoneType.Battlefield);
definedCards = CardLists.getValidCards(definedCards, keywordDefined, hostCard.getController(),
hostCard, null);
hostCard, stAb);
for (Card c : definedCards) {
final int cmc = c.getCMC();
String y = (input.replace(" from EachCMCAmongDefined", ":Card.cmcEQ"
@@ -294,12 +294,12 @@ public final class StaticAbilityContinuous {
@Override
public String apply(String input) {
if (hostCard.hasChosenColor()) {
input = input.replaceAll("ChosenColor", StringUtils.capitalize(hostCard.getChosenColor()));
input = input.replaceAll("chosenColor", hostCard.getChosenColor().toLowerCase());
if (stAb.hasChosenColor()) {
input = input.replaceAll("ChosenColor", StringUtils.capitalize(stAb.getChosenColor()));
input = input.replaceAll("chosenColor", stAb.getChosenColor().toLowerCase());
}
if (hostCard.hasChosenType()) {
input = input.replaceAll("ChosenType", hostCard.getChosenType());
if (stAb.hasChosenType()) {
input = input.replaceAll("ChosenType", stAb.getChosenType(0));
}
if (hostCard.hasChosenNumber()) {
input = input.replaceAll("ChosenNumber", String.valueOf(hostCard.getChosenNumber()));
@@ -377,10 +377,10 @@ public final class StaticAbilityContinuous {
Iterables.removeIf(addTypes, new Predicate<String>() {
@Override
public boolean apply(String input) {
if (input.equals("ChosenType") && !hostCard.hasChosenType()) {
if (input.equals("ChosenType") && !stAb.hasChosenType()) {
return true;
}
if (input.equals("ChosenType2") && !hostCard.hasChosenType2()) {
if (input.equals("ChosenType2") && stAb.getChosenType(1) == null) {
return true;
}
if (input.equals("ImprintedCreatureType")) {
@@ -401,11 +401,11 @@ public final class StaticAbilityContinuous {
addTypes = Lists.transform(addTypes, new Function<String, String>() {
@Override
public String apply(String input) {
if (hostCard.hasChosenType2()) {
input = input.replaceAll("ChosenType2", hostCard.getChosenType2());
if (stAb.getChosenType(1) != null) {
input = input.replaceAll("ChosenType2", stAb.getChosenType(1));
}
if (hostCard.hasChosenType()) {
input = input.replaceAll("ChosenType", hostCard.getChosenType());
if (stAb.getChosenType(0) != null) {
input = input.replaceAll("ChosenType", stAb.getChosenType(0));
}
return input;
}
@@ -419,7 +419,7 @@ public final class StaticAbilityContinuous {
Iterables.removeIf(removeTypes, new Predicate<String>() {
@Override
public boolean apply(String input) {
if (input.equals("ChosenType") && !hostCard.hasChosenType()) {
if (input.equals("ChosenType") && !stAb.hasChosenType()) {
return true;
}
return false;
@@ -458,7 +458,7 @@ public final class StaticAbilityContinuous {
if (params.containsKey("AddColor")) {
final String colors = params.get("AddColor");
if (colors.equals("ChosenColor")) {
addColors = CardUtil.getShortColorsString(hostCard.getChosenColors());
addColors = CardUtil.getShortColorsString(stAb.getChosenColors());
} else if (colors.equals("All")) {
addColors = "W U B R G";
} else {
@@ -469,7 +469,7 @@ public final class StaticAbilityContinuous {
if (params.containsKey("SetColor")) {
final String colors = params.get("SetColor");
if (colors.equals("ChosenColor")) {
addColors = CardUtil.getShortColorsString(hostCard.getChosenColors());
addColors = CardUtil.getShortColorsString(stAb.getChosenColors());
} else if (colors.equals("All")) {
addColors = "W U B R G";
} else {
@@ -504,7 +504,7 @@ public final class StaticAbilityContinuous {
if ("True".equals(look)) {
look = "You";
}
mayLookAt = AbilityUtils.getDefinedPlayers(hostCard, look, null);
mayLookAt = AbilityUtils.getDefinedPlayers(hostCard, look, stAb);
}
if (params.containsKey("MayPlay")) {
controllerMayPlay = true;
@@ -567,7 +567,7 @@ public final class StaticAbilityContinuous {
}
}
if (params.containsKey("ControlOpponentsSearchingLibrary")) {
Player cntl = Iterables.getFirst(AbilityUtils.getDefinedPlayers(hostCard, params.get("ControlOpponentsSearchingLibrary"), null), null);
Player cntl = Iterables.getFirst(AbilityUtils.getDefinedPlayers(hostCard, params.get("ControlOpponentsSearchingLibrary"), stAb), null);
p.addControlledWhileSearching(se.getTimestamp(), cntl);
}
@@ -619,8 +619,8 @@ public final class StaticAbilityContinuous {
if (changeColorWordsTo != null) {
final byte color;
if (changeColorWordsTo.equals("ChosenColor")) {
if (hostCard.hasChosenColor()) {
color = MagicColor.fromName(Iterables.getFirst(hostCard.getChosenColors(), null));
if (stAb.hasChosenColor()) {
color = MagicColor.fromName(stAb.getChosenColor());
} else {
color = 0;
}
@@ -766,7 +766,7 @@ public final class StaticAbilityContinuous {
}
CardCollectionView cardsIGainedAbilitiesFrom = game.getCardsIn(validZones);
cardsIGainedAbilitiesFrom = CardLists.getValidCards(cardsIGainedAbilitiesFrom, valids, hostCard.getController(), hostCard, null);
cardsIGainedAbilitiesFrom = CardLists.getValidCards(cardsIGainedAbilitiesFrom, valids, hostCard.getController(), hostCard, stAb);
for (Card c : cardsIGainedAbilitiesFrom) {
for (SpellAbility sa : c.getSpellAbilities()) {
@@ -936,7 +936,7 @@ public final class StaticAbilityContinuous {
final String[] strngs = params.get("Affected").split(",");
for (Player p : controller.getGame().getPlayersInTurnOrder()) {
if (p.isValid(strngs, controller, hostCard, null)) {
if (p.isValid(strngs, controller, hostCard, stAb)) {
players.add(p);
}
}
@@ -983,13 +983,13 @@ public final class StaticAbilityContinuous {
} else if (params.get("Affected").contains("EquippedBy")) {
affectedCards = new CardCollection(hostCard.getEquipping());
} else if (params.get("Affected").equals("EffectSource")) {
affectedCards = new CardCollection(AbilityUtils.getDefinedCards(hostCard, params.get("Affected"), null));
affectedCards = new CardCollection(AbilityUtils.getDefinedCards(hostCard, params.get("Affected"), stAb));
return affectedCards;
}
}
if (params.containsKey("Affected")) {
affectedCards = CardLists.getValidCards(affectedCards, params.get("Affected").split(","), controller, hostCard, null);
affectedCards = CardLists.getValidCards(affectedCards, params.get("Affected").split(","), controller, hostCard, stAb);
}
affectedCards.removeAll((List<?>) stAb.getIgnoreEffectCards());
return affectedCards;

View File

@@ -55,7 +55,6 @@ public class TriggerCounterRemoved extends Trigger {
* @param runParams*/
@Override
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
final Card addedTo = (Card) runParams.get(AbilityKey.Card);
final CounterType addedType = (CounterType) runParams.get(AbilityKey.CounterType);
final Integer addedNewCounterAmount = (Integer) runParams.get(AbilityKey.NewCounterAmount);

View File

@@ -55,7 +55,6 @@ public class TriggerCounterRemovedOnce extends Trigger {
* @param runParams*/
@Override
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
final Card removedFrom = (Card) runParams.get(AbilityKey.Card);
final CounterType removedType = (CounterType) runParams.get(AbilityKey.CounterType);
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Card))) {

View File

@@ -83,7 +83,14 @@ public class TriggerTapsForMana extends Trigger {
}
String produced = (String) prod;
if ("ChosenColor".equals(getParam("Produced"))) {
if (!this.getHostCard().hasChosenColor() || !produced.contains(MagicColor.toShortString(this.getHostCard().getChosenColor()))) {
boolean found = false;
for (String color : this.getChosenColors()) {
if (produced.contains(MagicColor.toShortString(color))) {
found = true;
break;
}
}
if (!found) {
return false;
}
} else if (!produced.contains(MagicColor.toShortString(this.getParam("Produced")))) {

View File

@@ -51,9 +51,6 @@ public enum TrackableProperty {
AssignedDamage(TrackableTypes.IntegerType),
LethalDamage(TrackableTypes.IntegerType),
ShieldCount(TrackableTypes.IntegerType),
ChosenType(TrackableTypes.StringType),
ChosenType2(TrackableTypes.StringType),
ChosenColors(TrackableTypes.StringListType),
ChosenCards(TrackableTypes.CardViewCollectionType),
ChosenNumber(TrackableTypes.StringType),
ChosenPlayer(TrackableTypes.PlayerViewType),

View File

@@ -482,7 +482,7 @@ public class GameSimulatorTest extends SimulationTestCase {
Player p = game.getPlayers().get(1);
Card bear = addCard(bearCardName, p);
Card hall = addCard("Hall of Triumph", p);
hall.setChosenColors(Lists.newArrayList("green"));
hall.setChosenColors(Lists.newArrayList("green"), hall.getFirstSpellAbility());
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
game.getAction().checkStateEffects(true);
assertEquals(3, bear.getNetToughness());

View File

@@ -80,7 +80,7 @@ import forge.util.collect.FCollectionView;
/**
* Default harmless implementation for tests.
* Test-specific behaviour can easily be added by mocking (parts of) this class.
*
*
* Note that the current PlayerController implementations seem to be responsible for handling some game logic,
* and even aside from that, they are theoretically capable of making illegal choices (which are then not blocked by the real game logic).
* Test cases that need to override the default behaviour of this class should make sure to do so in a way that does not invalidate their correctness.
@@ -281,7 +281,7 @@ public class PlayerControllerForTests extends PlayerController {
@Override
public CardCollection chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, CardCollection validCards, int min, int max) {
return chooseItems(validCards, min);
return chooseItems((CardCollectionView)validCards, min);
}
@Override
@@ -416,7 +416,7 @@ public class PlayerControllerForTests extends PlayerController {
@Override
public List<SpellAbility> chooseSpellAbilityToPlay() {
//TODO: This method has to return the spellability chosen by player
// It should not play the sa right from here. The code has been left as it is to quickly adapt to changed playercontroller interface
// It should not play the sa right from here. The code has been left as it is to quickly adapt to changed playercontroller interface
if (playerActions != null) {
CastSpellFromHandAction castSpellFromHand = playerActions.getNextActionIfApplicable(player, getGame(), CastSpellFromHandAction.class);
if (castSpellFromHand != null) {
@@ -470,7 +470,7 @@ public class PlayerControllerForTests extends PlayerController {
public byte chooseColor(String message, SpellAbility sa, ColorSet colors) {
return Iterables.getFirst(colors, MagicColor.WHITE);
}
@Override
public byte chooseColorAllowColorless(String message, Card card, ColorSet colors) {
return Iterables.getFirst(colors, (byte)0);
@@ -483,6 +483,13 @@ public class PlayerControllerForTests extends PlayerController {
return (CardCollection)items.subList(0, Math.max(amount, items.size()));
}
private <T> List<T> chooseItems(List<T> items, int amount) {
if (items == null || items.isEmpty()) {
return Lists.newArrayList();
}
return items.subList(0, Math.max(amount, items.size()));
}
private <T> T chooseItem(Iterable<T> items) {
if (items == null) {
return null;
@@ -497,8 +504,8 @@ public class PlayerControllerForTests extends PlayerController {
}
@Override
public String chooseSomeType(String kindOfType, SpellAbility sa, Collection<String> validTypes, List<String> invalidTypes, boolean isOptional) {
return chooseItem(validTypes);
public List<String> chooseSomeType(String kindOfType, SpellAbility sa, int min, int max, List<String> validTypes) {
return chooseItems(validTypes, min);
}
@Override
@@ -545,7 +552,7 @@ public class PlayerControllerForTests extends PlayerController {
ComputerUtil.playStack(sa, player, getGame());
}
}
private void prepareSingleSa(final Card host, final SpellAbility sa, boolean isMandatory){
if (sa.hasParam("TargetingPlayer")) {
Player targetingPlayer = AbilityUtils.getDefinedPlayers(host, sa.getParam("TargetingPlayer"), sa).get(0);
@@ -577,7 +584,7 @@ public class PlayerControllerForTests extends PlayerController {
} else {
ComputerUtil.playStack(tgtSA, player, getGame());
}
} else
} else
return false; // didn't play spell
}
return true;

View File

@@ -4,10 +4,8 @@ Types:Creature Angel
PT:5/6
K:Flying
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled cards to their owners' hands.
SVar:TrigExile:DB$ ChangeZone | TargetMin$ 0 | TargetMax$ 3 | IsCurse$ True | ValidTgts$ Creature.Other | TgtPrompt$ Choose another target creature | RememberChanged$ True | Origin$ Battlefield,Graveyard | Destination$ Exile
SVar:TrigReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Hand | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:TrigExile:DB$ ChangeZone | TargetMin$ 0 | TargetMax$ 3 | IsCurse$ True | ValidTgts$ Creature.Other | TgtPrompt$ Choose another target creature | Origin$ Battlefield,Graveyard | Destination$ Exile
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled cards to their owners' hands.
SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Hand
SVar:PlayMain1:TRUE
SVar:Picture:http://www.wizards.com/global/images/magic/general/angel_of_serenity.jpg
Oracle:Flying\nWhen Angel of Serenity enters the battlefield, you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards.\nWhen Angel of Serenity leaves the battlefield, return the exiled cards to their owners' hands.

View File

@@ -2,15 +2,9 @@ Name:Ashiok, Nightmare Weaver
ManaCost:1 U B
Types:Legendary Planeswalker Ashiok
Loyalty:3
A:AB$ Dig | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | ValidTgts$ Opponent | DigNum$ 3 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SpellDescription$ Exile the top three cards of target opponent's library.
A:AB$ ChooseCard | Cost$ SubCounter<X/LOYALTY> | Choices$ Creature.cmcEQX+IsRemembered+ExiledWithSource | ChoiceZone$ Exile | Planeswalker$ True | SubAbility$ DBChangeZone | AILogic$ Ashiok | SpellDescription$ Put a creature card with converted mana cost X exiled with CARDNAME onto the battlefield under your control. That creature is a Nightmare in addition to its other types.
SVar:DBChangeZone:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Exile | Destination$ Battlefield | ChangeType$ Creature.cmcEQX+IsRemembered+ExiledWithSource | ChangeNum$ 1 | GainControl$ True | SubAbility$ DBAnimate
SVar:DBAnimate:DB$ Animate | Defined$ ChosenCard | Types$ Nightmare | Permanent$ True | SubAbility$ DBCleanMinus
SVar:DBCleanMinus:DB$ Cleanup | ForgetDefined$ ChosenCard | ClearChosenCard$ True
A:AB$ Dig | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | ValidTgts$ Opponent | DigNum$ 3 | ChangeNum$ All | DestinationZone$ Exile | SpellDescription$ Exile the top three cards of target opponent's library.
A:AB$ ChangeZone | Cost$ SubCounter<X/LOYALTY> | Planeswalker$ True | Hidden$ True | Origin$ Exile | Destination$ Battlefield | ChangeType$ Creature.cmcEQX+ExiledWithSource | ChangeNum$ 1 | GainControl$ True | AnimateSubAbility$ DBAnimate | AILogic$ Ashiok | Mandatory$ True | StackDescription$ SpellDescription | SpellDescription$ Put a creature card exiled with CARDNAME onto the battlefield under your control.
SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Nightmare | Permanent$ True
SVar:X:Count$xPaid
A:AB$ ChangeZoneAll | Cost$ SubCounter<10/LOYALTY> | ChangeType$ Card.OppCtrl | Origin$ Graveyard,Hand | Destination$ Exile | RememberChanged$ True | Planeswalker$ True | Ultimate$ True | SpellDescription$ Exile all cards from all opponents' hands and graveyards.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
A:AB$ ChangeZoneAll | Cost$ SubCounter<10/LOYALTY> | ChangeType$ Card.OppCtrl | Origin$ Graveyard,Hand | Destination$ Exile | Planeswalker$ True | Ultimate$ True | SpellDescription$ Exile all cards from all opponents' hands and graveyards.
Oracle:[+2]: Exile the top three cards of target opponent's library.\n[X]: Put a creature card with converted mana cost X exiled with Ashiok, Nightmare Weaver onto the battlefield under your control. That creature is a Nightmare in addition to its other types.\n[10]: Exile all cards from all opponents' hands and graveyards.

View File

@@ -3,9 +3,8 @@ ManaCost:2 U U
Types:Enchantment
K:Flash
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile target spell.
S:Mode$ CantBeCast | ValidCard$ Card.nonLand+sharesNameWith Remembered.ExiledWithSource | Caster$ Opponent | Description$ Your opponents can't cast spells with the same name as the card exiled by CARDNAME.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigBounce | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled card to its owner's hand.
SVar:TrigExile:DB$ChangeZone | TargetType$ Spell | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Mandatory$ True | Destination$ Exile | IsCurse$ True | TgtPrompt$ Choose target spell | RememberChanged$ True
SVar:TrigBounce:DB$ChangeZone | Origin$ Exile | Destination$ Hand | Defined$ Remembered | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:TrigExile:DB$ ChangeZone | TargetType$ Spell | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Mandatory$ True | Destination$ Exile | IsCurse$ True | TgtPrompt$ Choose target spell
S:Mode$ CantBeCast | ValidCard$ Card.nonLand+sharesNameWith ExiledWith | Caster$ Opponent | Description$ Your opponents can't cast spells with the same name as the card exiled by CARDNAME.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigBounce | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled card to its owner's hand.
SVar:TrigBounce:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Hand
Oracle:Flash\nWhen Ashiok's Erasure enters the battlefield, exile target spell.\nYour opponents can't cast spells with the same name as the exiled card.\nWhen Ashiok's Erasure leaves the battlefield, return the exiled card to its owner's hand.

View File

@@ -2,16 +2,11 @@ Name:Azor's Gateway
ManaCost:2
Types:Legendary Artifact
A:AB$ Draw | Cost$ 1 T | NumCards$ 1 | SubAbility$ DBExile | SpellDescription$ Draw a card, then exile a card from your hand. If cards with five or more different converted mana costs are exiled with CARDNAME, you gain 5 life, untap CARDNAME, and transform it.
SVar:DBExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | RememberChanged$ True | SubAbility$ DBGainLife
SVar:DBExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | SubAbility$ DBGainLife
SVar:DBGainLife:DB$ GainLife | LifeAmount$ 5 | SubAbility$ DBUntap | ConditionCheckSVar$ Y | ConditionSVarCompare$ GE5
SVar:DBUntap:DB$ Untap | Defined$ Self | SubAbility$ DBTransform | ConditionCheckSVar$ Y | ConditionSVarCompare$ GE5
SVar:DBTransform:DB$ SetState | Defined$ Self | Mode$ Transform | ConditionCheckSVar$ Y | ConditionSVarCompare$ GE5
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:Y:Remembered$DifferentCMC
SVar:Picture:http://www.wizards.com/global/images/magic/general/azors_gateway.jpg
SVar:Y:ExiledWith$DifferentCMC
AlternateMode:DoubleFaced
Oracle:{1}, {T}: Draw a card, then exile a card from your hand. If cards with five or more different converted mana costs are exiled with Azor's Gateway, you gain 5 life, untap Azor's Gateway, and transform it.
@@ -23,5 +18,4 @@ Colors:colorless
Types:Land
A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ X | SpellDescription$ Add X mana of any one color, where X is your life total.
SVar:X:Count$YourLifeTotal
SVar:Picture:http://www.wizards.com/global/images/magic/general/sanctum_of_the_sun.jpg
Oracle:(Transforms from Azor's Gateway.)\n{T}: Add X mana of any one color, where X is your life total.

View File

@@ -3,13 +3,7 @@ ManaCost:1 U B
Types:Creature Human Rogue
PT:0/3
A:AB$ Draw | Cost$ T | NumCards$ 1 | SubAbility$ DBExile | SpellDescription$ Draw a card, then exile a card from your hand face down.
SVar:DBExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | ExileFaceDown$ True | Mandatory$ True | RememberChanged$ True
S:Mode$ Continuous | Affected$ Card.IsRemembered+ExiledWithSource | AffectedZone$ Exile | MayLookAt$ You | Description$ You may look at cards exiled with CARDNAME.
A:AB$ ChooseCard | Cost$ U B T | Defined$ You | Amount$ 1 | Mandatory$ True | AILogic$ AtLeast1 | ChoiceTitle$ Choose a card to put into your hand | Choices$ Card.IsRemembered+ExiledWithSource | ChoiceZone$ Exile | SubAbility$ MoveChosen | SpellDescription$ Return a card exiled with CARDNAME to its owner's hand.
SVar:MoveChosen:DB$ ChangeZone | Origin$ Exile | Destination$ Hand | Defined$ ChosenCard | ForgetChanged$ True | SubAbility$ DBCleanupChosen
SVar:DBCleanupChosen:DB$ Cleanup | ClearChosenCard$ True
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:DBExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | ExileFaceDown$ True | Mandatory$ True
S:Mode$ Continuous | Affected$ Card.ExiledWithSource | AffectedZone$ Exile | MayLookAt$ You | Description$ You may look at cards exiled with CARDNAME.
A:AB$ ChangeZone | Cost$ U B T | Hidden$ True | Origin$ Exile | Destination$ Hand | ChangeType$ Creature.ExiledWithSource | ChangeNum$ 1 | Mandatory$ True | StackDescription$ SpellDescription | SpellDescription$ Return a card exiled with CARDNAME to its owner's hand.
Oracle:{T}: Draw a card, then exile a card from your hand face down.\nYou may look at cards exiled with Bane Alley Broker.\n{U}{B}, {T}: Return a card exiled with Bane Alley Broker to its owner's hand.

View File

@@ -5,4 +5,4 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.S
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Permanent.nonLand+OppCtrl | TgtPrompt$ Select target nonland permanent an opponent controls | UntilHostLeavesPlay$ True
SVar:PlayMain1:TRUE
SVar:OblivionRing:TRUE
Oracle:When Banishing Light enters the battlefield, exile target nonland permanent an opponent controls until Banishing Light leaves the battlefield.
Oracle:When Banishing Light enters the battlefield, exile target nonland permanent an opponent controls until Banishing Light leaves the battlefield. (That permanent returns under its owner's control.)

View File

@@ -3,17 +3,11 @@ ManaCost:3 W
Types:Creature Vampire Cleric
PT:1/1
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile target creature an opponent controls until CARDNAME leaves the battlefield.
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | ConditionPresent$ Card.Self | RememberChanged$ True | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | Triggers$ ComeBack | RememberObjects$ Targeted | ImprintCards$ Self | ConditionPresent$ Card.Self | Duration$ Permanent | ForgetOnMoved$ Exile
SVar:ComeBack:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.IsImprinted | Execute$ TrigReturn | TriggerZones$ Command | TriggerController$ TriggeredCardController | Static$ True | TriggerDescription$ That creature is exiled until EFFECTSOURCE leaves the battlefield
SVar:TrigReturn:DB$ ChangeZoneAll | Origin$ Exile | Destination$ Battlefield | ChangeType$ Card.IsRemembered | SubAbility$ ExileSelf
SVar:ExileSelf:DB$ ChangeZone | Origin$ Command | Destination$ Exile | Defined$ Self
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | UntilHostLeavesPlay$ True
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks, target Vampire gets +X/+X until end of turn, where X is the power of the exiled card.
SVar:TrigPump:DB$ Pump | ValidTgts$ Permanent.Vampire | TgtPrompt$ Select target Vampire | NumAtt$ X | NumDef$ X
SVar:X:Remembered$CardPower
SVar:X:ExiledWith$CardPower
// Release notes indicate that this effect should work with Vehicle cards.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:PlayMain1:TRUE
DeckHints:Type$Vampire
Oracle:When Bishop of Binding enters the battlefield, exile target creature an opponent controls until Bishop of Binding leaves the battlefield.\nWhenever Bishop of Binding attacks, target Vampire gets +X/+X until end of turn, where X is the power of the exiled card.
Oracle:When Bishop of Binding enters the battlefield, exile target creature an opponent controls until Bishop of Binding leaves the battlefield.\nWhenever Bishop of Binding attacks, target Vampire gets +X/+X until end of turn, where X is the power of the exiled card.

View File

@@ -4,12 +4,9 @@ Types:Artifact Creature Construct
PT:1/1
K:Haste
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigExile | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, exile the top card of your library face down. (You can't look at it.)
SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | ExileFaceDown$ True | NoReveal$ True
A:AB$ ChangeZoneAll | Cost$ R Discard<0/Hand> Sac<1/CARDNAME> | ChangeType$ Card.IsRemembered+ExiledWithSource | Origin$ Exile | Destination$ Hand | AILogic$ DiscardAllAndRetExiled.minAdv2 | SpellDescription$ Put all cards exiled with CARDNAME into their owners' hands.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
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.

View File

@@ -4,7 +4,7 @@ Types:Legendary Creature Human Minion
PT:3/3
S:Mode$ Continuous | Affected$ Creature.Nightmare | AddPower$ 1 | AddToughness$ 1 | Description$ Nightmare creatures get +1/+1.
A:AB$ ChangeZone | Cost$ B B B PayLife<3> | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | TgtPrompt$ Select target creature card in a graveyard | ValidTgts$ Creature | ChangeNum$ 1 | AnimateSubAbility$ DBAnimate | SpellDescription$ Put target creature card from a graveyard onto the battlefield under your control. That creature is black and is a Nightmare in addition to its other creature types.
SVar:DBAnimate:DB$ Animate | Defined$ Targeted | Types$ Nightmare | Colors$ Black | Permanent$ True | OverwriteColors$ True
SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Nightmare | Colors$ Black | Permanent$ True | OverwriteColors$ True
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigExile | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, exile all Nightmares.
SVar:TrigExile:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ Nightmare
SVar:PlayMain1:TRUE

View File

@@ -2,13 +2,8 @@ Name:Chrome Mox
ManaCost:0
Types:Artifact
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ Imprint — When CARDNAME enters the battlefield, you may exile a nonartifact, nonland card from your hand.
SVar:TrigExile:DB$ChangeZone | Imprint$ True | Origin$ Hand | Destination$ Exile | ChangeType$ Card.nonArtifact+nonLand | ChangeNum$ 1
A:AB$ ManaReflected | Cost$ T | Valid$ Defined.Imprinted | ColorOrType$ Color | ReflectProperty$ Is | SpellDescription$ Add one mana of any of the exiled card's colors.
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsImprinted+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
SVar:TrigExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card.nonArtifact+nonLand | ChangeNum$ 1
A:AB$ ManaReflected | Cost$ T | Valid$ ExiledWith | ColorOrType$ Color | ReflectProperty$ Is | SpellDescription$ Add one mana of any of the exiled card's colors.
SVar:NeedsToPlayVar:Z GE1
SVar:Z:Count$ValidHand Card.nonArtifact+nonColorless+nonLand+YouOwn
SVar:Picture:http://www.wizards.com/global/images/magic/general/chrome_mox.jpg
Oracle:Imprint — When Chrome Mox enters the battlefield, you may exile a nonartifact, nonland card from your hand.\n{T}: Add one mana of any of the exiled card's colors.
Oracle:Imprint — When Chrome Mox enters the battlefield, you may exile a nonartifact, nonland card from your hand.\n{T}: Add one mana of any of the exiled card's colors.

View File

@@ -4,10 +4,6 @@ Types:Legendary Creature Human Wizard
PT:2/3
T:Mode$ SpellCast | ValidCard$ Card.Blue | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ ExileTop | TriggerDescription$ Whenever you cast a blue spell, exile the top card of target player's library.
T:Mode$ SpellCast | ValidCard$ Card.Black | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ ExileTop | TriggerDescription$ Whenever you cast a black spell, exile the top card of target player's library.
SVar:ExileTop:DB$ Dig | DigNum$ 1 | ChangeNum$ All | ValidTgts$ Player | TgtPrompt$ Choose a player | DestinationZone$ Exile | RememberChanged$ True
S:Mode$ CantBeCast | ValidCard$ Card.nonLand+sharesNameWith Remembered.ExiledWithSource | Caster$ Opponent | Description$ Your opponents can't cast spells with the same name as a card exiled with CARDNAME.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:ExileTop:DB$ Dig | DigNum$ 1 | ChangeNum$ All | ValidTgts$ Player | TgtPrompt$ Choose a player | DestinationZone$ Exile
S:Mode$ CantBeCast | ValidCard$ Card.nonLand+sharesNameWith ExiledWith | Caster$ Opponent | Description$ Your opponents can't cast spells with the same name as a card exiled with CARDNAME.
Oracle:Whenever you cast a blue spell, exile the top card of target player's library.\nWhenever you cast a black spell, exile the top card of target player's library.\nYour opponents can't cast spells with the same name as a card exiled with Circu, Dimir Lobotomist.

View File

@@ -3,11 +3,9 @@ ManaCost:5
Types:Artifact Creature Shapeshifter
PT:2/2
T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ Imprint — When Clone Shell enters the battlefield, look at the top four cards of your library, exile one face down, then put the rest on the bottom of your library in any order.
SVar:TrigDig:DB$ Dig | Defined$ You | DigNum$ 4 | DestinationZone$ Exile | ExileFaceDown$ True | Imprint$ True
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigFaceUp | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control.
SVar:TrigFaceUp:DB$ SetState | Defined$ Imprinted | SubAbility$ DBChangeZone | Mode$ TurnFace
SVar:DBChangeZone:DB$ ChangeZone | Defined$ Imprinted | Origin$ Exile | Destination$ Battlefield | ConditionDefined$ Imprinted | ConditionPresent$ Creature | GainControl$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
SVar:TrigDig:DB$ Dig | Defined$ You | DigNum$ 4 | DestinationZone$ Exile | ExileFaceDown$ True
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigFaceUp | TriggerDescription$ When CARDNAME dies, turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control.
SVar:TrigFaceUp:DB$ SetState | Defined$ ExiledWith | Mode$ TurnFace | SubAbility$ DBChangeZone
SVar:DBChangeZone:DB$ ChangeZone | Defined$ ExiledWith.Creature | Origin$ Exile | Destination$ Battlefield
SVar:SacMe:5
SVar:Picture:http://www.wizards.com/global/images/magic/general/clone_shell.jpg
Oracle:Imprint — When Clone Shell enters the battlefield, look at the top four cards of your library, exile one face down, then put the rest on the bottom of your library in any order.\nWhen Clone Shell dies, turn the exiled card face up. If it's a creature card, put it onto the battlefield under your control.

View File

@@ -1,13 +1,8 @@
Name:Cold Storage
ManaCost:4
Types:Artifact
A:AB$ ChangeZone | Cost$ 3 | ValidTgts$ Creature.YouCtrl | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you control | RememberTargets$ True | SpellDescription$ Exile target creature you control.
A:AB$ ChangeZoneAll | Cost$ Sac<1/CARDNAME> | ChangeType$ Card.Creature+IsRemembered+ExiledWithSource | Origin$ Exile | Destination$ Battlefield | GainControl$ True | SubAbility$ DBCleanup | SpellDescription$ Return each creature card exiled with CARDNAME to the battlefield under your control.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
A:AB$ ChangeZone | Cost$ 3 | ValidTgts$ Creature.YouCtrl | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you control | SpellDescription$ Exile target creature you control.
A:AB$ ChangeZone | Cost$ Sac<1/CARDNAME> | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield | GainControl$ True | SpellDescription$ Return each creature card exiled with CARDNAME to the battlefield under your control.
SVar:NonStackingEffect:True
AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/cold_storage.jpg
Oracle:{3}: Exile target creature you control.\nSacrifice Cold Storage: Return each creature card exiled with Cold Storage to the battlefield under your control.

View File

@@ -1,14 +1,10 @@
Name:Colfenor's Plans
ManaCost:2 B B
Types:Enchantment
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile the top seven cards of your library face down. You may look at the cards exiled with CARDNAME, and you may play lands and cast spells from among those cards.
SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 7 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | ExileFaceDown$ True | NoReveal$ True
S:Mode$ Continuous | Affected$ Card.IsRemembered+ExiledWithSource | AffectedZone$ Exile | MayPlay$ True | MayLookAt$ You | Description$ You may look at the cards exiled with CARDNAME, and you may play lands and cast spells from among those cards.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile the top seven cards of your library face down.
SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 7 | ChangeNum$ All | DestinationZone$ Exile | ExileFaceDown$ True | NoReveal$ True
R:Event$ BeginPhase | ActiveZones$ Battlefield | ValidPlayer$ You | Phase$ Draw | Skip$ True | Description$ Skip your draw step.
S:Mode$ Continuous | Affected$ Card.ExiledWithSource | AffectedZone$ Exile | MayPlay$ True | MayLookAt$ You | Description$ You may look at the cards exiled with CARDNAME, and you may play lands and cast spells from among those cards.
S:Mode$ CantBeCast | ValidCard$ Card | Caster$ You | NumLimitEachTurn$ 1 | Description$ You can't cast more than one spell each turn.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
AI:RemoveDeck:All
Oracle:When Colfenor's Plans enters the battlefield, exile the top seven cards of your library face down.\nYou may look at the cards exiled with Colfenor's Plans, and you may play lands and cast spells from among those cards.\nSkip your draw step.\nYou can't cast more than one spell each turn.

View File

@@ -2,12 +2,7 @@ Name:Dark Impostor
ManaCost:2 B
Types:Creature Vampire Assassin
PT:2/2
S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ Battlefield | GainsAbilitiesOf$ Creature.IsRemembered+ExiledWithSource | GainsAbilitiesOfZones$ Exile | Description$ CARDNAME has all activated abilities of all creature cards exiled with it.
A:AB$ ChangeZone | Cost$ 4 B B | ValidTgts$ Creature | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature | SubAbility$ DBCounter | RememberChanged$ True | SpellDescription$ Exile target creature and put a +1/+1 counter on CARDNAME.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ Battlefield | GainsAbilitiesOf$ Creature.ExiledWithSource | GainsAbilitiesOfZones$ Exile | Description$ CARDNAME has all activated abilities of all creature cards exiled with it.
A:AB$ ChangeZone | Cost$ 4 B B | ValidTgts$ Creature | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature | SubAbility$ DBCounter | SpellDescription$ Exile target creature and put a +1/+1 counter on CARDNAME.
SVar:DBCounter:DB$PutCounter | CounterType$ P1P1 | CounterNum$ 1 | Defined$ Self
SVar:Picture:http://www.wizards.com/global/images/magic/general/dark_impostor.jpg
Oracle:{4}{B}{B}: Exile target creature and put a +1/+1 counter on Dark Impostor.\nDark Impostor has all activated abilities of all creature cards exiled with it.

View File

@@ -2,11 +2,11 @@ Name:Day of the Dragons
ManaCost:4 U U U
Types:Enchantment
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile all creatures you control. Then create that many 5/5 red Dragon creature tokens with flying. When CARDNAME leaves the battlefield, sacrifice all Dragons you control. Then return the exiled cards to the battlefield under your control.
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Execute$ TrigSacrifice | Secondary$ True | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, sacrifice all Dragons you control. Then return the exiled cards to the battlefield under your control.
SVar:TrigExile:DB$ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | ForgetOtherRemembered$ True | ChangeType$ Creature.YouCtrl | SubAbility$ DBToken
SVar:TrigSacrifice:DB$SacrificeAll | ValidCards$ Dragon.YouCtrl | SubAbility$ DBReturn
SVar:DBToken:DB$Token | TokenAmount$ X | TokenScript$ r_5_5_dragon_flying | TokenOwner$ You | LegacyImage$ r 5 5 dragon flying scg
SVar:DBReturn:DB$ChangeZoneAll | Origin$ Exile | Destination$ Battlefield | ChangeType$ Card.IsRemembered
SVar:TrigExile:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | ChangeType$ Creature.YouCtrl | SubAbility$ DBToken
SVar:DBToken:DB$ Token | TokenAmount$ X | TokenScript$ r_5_5_dragon_flying | TokenOwner$ You | LegacyImage$ r 5 5 dragon flying scg | References$ X | SubAbility$ DBCleanup
SVar:X:Remembered$Amount
SVar:Picture:http://www.wizards.com/global/images/magic/general/day_of_the_dragons.jpg
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Execute$ TrigSacrifice | Secondary$ True | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, sacrifice all Dragons you control. Then return the exiled cards to the battlefield under your control.
SVar:TrigSacrifice:DB$ SacrificeAll | ValidCards$ Dragon.YouCtrl | SubAbility$ DBReturn
SVar:DBReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield
Oracle:When Day of the Dragons enters the battlefield, exile all creatures you control. Then create that many 5/5 red Dragon creature tokens with flying.\nWhen Day of the Dragons leaves the battlefield, sacrifice all Dragons you control. Then return the exiled cards to the battlefield under your control.

View File

@@ -4,5 +4,6 @@ Types:Creature Vedalken Wizard
PT:1/3
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile target nonland permanent an opponent controls and all other nonland permanents that player controls with the same name as that permanent until CARDNAME leaves the battlefield.
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Permanent.nonLand+OppCtrl | TgtPrompt$ Select target nonland permanent an opponent controls | SubAbility$ DBChangeZoneAll | UntilHostLeavesPlay$ True
SVar:DBChangeZoneAll:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ Permanent.nonLand+NotDefinedTargeted+sharesNameWith Targeted+ControlledBy TargetedOrController | UntilHostLeavesPlay$ True
SVar:DBChangeZoneAll:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ Card.sharesNameWith ExiledWith+ControlledBy TargetedOrController | UntilHostLeavesPlay$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
Oracle:When Deputy of Detention enters the battlefield, exile target nonland permanent an opponent controls and all other nonland permanents that player controls with the same name as that permanent until Deputy of Detention leaves the battlefield.

View File

@@ -2,10 +2,9 @@ Name:Detention Sphere
ManaCost:1 W U
Types:Enchantment
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, you may exile target nonland permanent not named CARDNAME and all other permanents with the same name as that permanent.
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Permanent.nonLand+notnamedDetention Sphere | TgtPrompt$ Choose target nonland permanent not named Detention Sphere | RememberTargets$ True | ForgetOtherTargets$ True | SubAbility$ DBChangeZoneAll
SVar:DBChangeZoneAll:DB$ ChangeZoneAll | ChangeType$ Remembered.sameName | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Permanent.nonLand+notnamedDetention Sphere | TgtPrompt$ Choose target nonland permanent not named Detention Sphere | SubAbility$ DBChangeZoneAll
SVar:DBChangeZoneAll:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ Card.sharesNameWith ExiledWith
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled cards to the battlefield under their owner's control.
SVar:TrigReturn:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ Exile | Destination$ Battlefield
SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield
SVar:OblivionRing:TRUE
SVar:Picture:http://www.wizards.com/global/images/magic/general/detention_sphere.jpg
Oracle:When Detention Sphere enters the battlefield, you may exile target nonland permanent not named Detention Sphere and all other permanents with the same name as that permanent.\nWhen Detention Sphere leaves the battlefield, return the exiled cards to the battlefield under their owner's control.

View File

@@ -3,14 +3,8 @@ ManaCost:3 U
Types:Creature Human Wizard
PT:1/1
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, you may exile an instant card from your hand.
SVar:TrigExile:DB$ ChangeZone | RememberChanged$ True | Origin$ Hand | Destination$ Exile | ChangeType$ Instant | ChangeNum$ 1
A:AB$ Play | Cost$ X T | Valid$ Card.IsRemembered+ExiledWithSource | ValidZone$ Exile | Amount$ All | CopyOnce$ True | WithoutManaCost$ True | Optional$ True | CopyCard$ True | SpellDescription$ Copy the exiled card. You may cast the copy without paying its mana cost. X is the converted mana cost of the exiled card.
SVar:X:Remembered$CardManaCost
T:Mode$ ChangesZone | ValidCard$ Card.IsRemembered+ExiledWithSource | Origin$ Exile | Destination$ Any | Execute$ ForgetCard | Static$ True
SVar:ForgetCard:DB$ Cleanup | ForgetDefined$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
#Amount$ All | CopyOnce$ True for Strionic Resonator
SVar:TrigExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Instant | ChangeNum$ 1
A:AB$ Play | Cost$ X T | Valid$ Card.ExiledWithSource | ValidZone$ Exile | Amount$ All | CopyOnce$ True | WithoutManaCost$ True | Optional$ True | CopyCard$ True | SpellDescription$ Copy the exiled card. You may cast the copy without paying its mana cost. X is the converted mana cost of the exiled card.
SVar:X:ExiledWith$CardManaCost
AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/elite_arcanist.jpg
Oracle:When Elite Arcanist enters the battlefield, you may exile an instant card from your hand.\n{X}, {T}: Copy the exiled card. You may cast the copy without paying its mana cost. X is the converted mana cost of the exiled card.

View File

@@ -2,11 +2,6 @@ Name:Endless Sands
ManaCost:no cost
Types:Land Desert
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
A:AB$ ChangeZone | Cost$ 2 T | ValidTgts$ Creature.YouCtrl | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you control | RememberTargets$ True | SpellDescription$ Exile target creature you control.
A:AB$ ChangeZoneAll | Cost$ 4 T Sac<1/CARDNAME> | ChangeType$ Card.Creature+IsRemembered+ExiledWithSource | Origin$ Exile | Destination$ Battlefield | SubAbility$ DBCleanup | SpellDescription$ Return each creature card exiled with CARDNAME to the battlefield under its owner's control.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/endless_sands.jpg
Oracle:{T}: Add {C}.\n{2}, {T}: Exile target creature you control.\n{4}, {T}, Sacrifice Endless Sands: Return each creature card exiled with Endless Sands to the battlefield under its owner's control.
A:AB$ ChangeZone | Cost$ 2 T | ValidTgts$ Creature.YouCtrl | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you control | SpellDescription$ Exile target creature you control.
A:AB$ ChangeZone | Cost$ 4 T Sac<1/CARDNAME> | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield | SpellDescription$ Return each creature card exiled with CARDNAME to the battlefield under its owner's control.
Oracle:{T}: Add {C}.\n{2}, {T}: Exile target creature you control.\n{4}, {T}, Sacrifice Endless Sands: Return each creature card exiled with Endless Sands to the battlefield under its owner's control.

View File

@@ -2,11 +2,6 @@ Name:Exclusion Ritual
ManaCost:4 W W
Types:Enchantment
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigExile | TriggerDescription$ Imprint — When CARDNAME enters the battlefield, exile target nonland permanent.
SVar:TrigExile:DB$ ChangeZone | Imprint$ True | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | Origin$ Battlefield | Destination$ Exile
S:Mode$ CantBeCast | ValidCard$ Card.sharesNameWith Imprinted | Description$ Players can't cast spells with the same name as the exiled card.
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSource | Origin$ Exile | Execute$ DBForget | Static$ True
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/exclusion_ritual.jpg
Oracle:Imprint — When Exclusion Ritual enters the battlefield, exile target nonland permanent.\nPlayers can't cast spells with the same name as the exiled card.
SVar:TrigExile:DB$ ChangeZone | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | Origin$ Battlefield | Destination$ Exile
S:Mode$ CantBeCast | ValidCard$ Card.sharesNameWith ExiledWith | Description$ Players can't cast spells with the same name as the exiled card.
Oracle:Imprint — When Exclusion Ritual enters the battlefield, exile target nonland permanent.\nPlayers can't cast spells with the same name as the exiled card.

View File

@@ -3,11 +3,8 @@ ManaCost:2 B B
Types:Creature Nightmare Horror
PT:2/3
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile target creature.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled card to the battlefield under its owner's control.
SVar:TrigExile:DB$ChangeZone | TargetMin$ 1 | IsCurse$ True | ValidTgts$ Creature.Other | TgtPrompt$ Choose target creature other than Faceless Butcher. | RememberTargets$ True | ForgetOtherTargets$ True | Origin$ Battlefield | Destination$ Exile
SVar:TrigReturn:DB$ChangeZoneAll | ChangeType$ Card.IsRemembered+ExiledWithSource | Origin$ Exile | Destination$ Battlefield | SubAbility$ DBCleanup
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled card to the battlefield under its owner's control.
SVar:TrigExile:DB$ ChangeZone | IsCurse$ True | ValidTgts$ Creature.Other | TgtPrompt$ Choose target creature other than CARDNAME. | Origin$ Battlefield | Destination$ Exile
SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield
SVar:PlayMain1:TRUE
Oracle:When Faceless Butcher enters the battlefield, exile target creature other than Faceless Butcher.\nWhen Faceless Butcher leaves the battlefield, return the exiled card to the battlefield under its owner's control.

View File

@@ -3,10 +3,10 @@ ManaCost:2 B
Types:Creature Nightmare Horror
PT:2/1
K:Shadow
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile another target creature with shadow.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled card to the battlefield under its owner's control.
SVar:TrigExile:DB$ChangeZone | TargetMin$ 1 | IsCurse$ True | ValidTgts$ Creature.withShadow+Other | TgtPrompt$ Choose target creature with shadow other than Faceless Devourer. | RememberTargets$ True | ForgetOtherTargets$ True | Origin$ Battlefield | Destination$ Exile
SVar:TrigReturn:DB$ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield
SVar:TrigExile:DB$ ChangeZone | TargetMin$ 1 | IsCurse$ True | ValidTgts$ Creature.withShadow+Other | TgtPrompt$ Choose target creature with shadow other than Faceless Devourer. | Origin$ Battlefield | Destination$ Exile
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled card to the battlefield under its owner's control.
SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield
SVar:PlayMain1:TRUE
SVar:Picture:http://www.wizards.com/global/images/magic/general/faceless_devourer.jpg
Oracle:Shadow (This creature can block or be blocked by only creatures with shadow.)\nWhen Faceless Devourer enters the battlefield, exile another target creature with shadow.\nWhen Faceless Devourer leaves the battlefield, return the exiled card to the battlefield under its owner's control.

View File

@@ -3,9 +3,8 @@ ManaCost:1 W W
Types:Creature Human Cleric
PT:1/3
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield, you may exile another target creature.
SVar:TrigExile:DB$ChangeZone | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | ForgetOtherRemembered$ True | ValidTgts$ Creature.Other | TgtPrompt$ Select target creature
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled card to the battlefield under its owner's control.
SVar:TrigReturn:DB$ChangeZone | Origin$ Exile | Destination$ Battlefield | Defined$ DirectRemembered
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Creature.Other | TgtPrompt$ Select target creature
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled card to the battlefield under its owner's control.
SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield
SVar:PlayMain1:TRUE
SVar:Picture:http://www.wizards.com/global/images/magic/general/fiend_hunter.jpg
Oracle:When Fiend Hunter enters the battlefield, you may exile another target creature.\nWhen Fiend Hunter leaves the battlefield, return the exiled card to the battlefield under its owner's control.

View File

@@ -6,12 +6,7 @@ T:Mode$ Blocks | ValidCard$ Card.EquippedBy | ValidBlocked$ Creature | Execute$
T:Mode$ AttackerBlocked | ValidCard$ Card.EquippedBy | ValidBlocker$ Creature | Execute$ TrigChooseBlockers | OptionalDecider$ You | Secondary$ True | TriggerDescription$ Whenever equipped creature blocks or becomes blocked by one or more creatures, you may exile one of those creatures.
SVar:TrigChooseAttackers:DB$ ChooseCard | DefinedCards$ TriggeredAttackers | SubAbility$ DBExile
SVar:TrigChooseBlockers:DB$ ChooseCard | DefinedCards$ TriggeredBlockers | SubAbility$ DBExile
SVar:DBExile:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True
S:Mode$ CantBeCast | ValidCard$ Card.nonLand+sharesNameWith Remembered.ExiledWithSource | Caster$ Opponent | Description$ Your opponents can't cast spells with the same name as a card exiled with CARDNAME.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Execute$ DBCleanup | Static$ True | Secondary$ True | TriggerDescription$ Forget all remembered cards.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:DBExile:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Exile
S:Mode$ CantBeCast | ValidCard$ Card.nonLand+sharesNameWith ExiledWith | Caster$ Opponent | Description$ Your opponents can't cast spells with the same name as a card exiled with CARDNAME.
K:Equip:3
SVar:Picture:http://www.wizards.com/global/images/magic/general/godsend.jpg
Oracle:Equipped creature gets +3/+3.\nWhenever equipped creature blocks or becomes blocked by one or more creatures, you may exile one of those creatures.\nYour opponents can't cast spells with the same name as a card exiled with Godsend.\nEquip {3}

View File

@@ -3,8 +3,7 @@ ManaCost:2 B
Types:Creature Nightmare Horror
PT:2/2
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile up to two target cards from a single graveyard.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled cards to their owner's graveyard.
SVar:TrigExile:DB$ChangeZone | TargetMin$ 0 | TargetMax$ 2 | TargetsFromSingleZone$ True | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card | RememberTargets$ True | ForgetOtherTargets$ True
SVar:TrigReturn:DB$ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Graveyard
SVar:Picture:http://www.wizards.com/global/images/magic/general/gravegouger.jpg
SVar:TrigExile:DB$ ChangeZone | TargetMin$ 0 | TargetMax$ 2 | TargetsFromSingleZone$ True | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled cards to their owner's graveyard.
SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Graveyard
Oracle:When Gravegouger enters the battlefield, exile up to two target cards from a single graveyard.\nWhen Gravegouger leaves the battlefield, return the exiled cards to their owner's graveyard.

View File

@@ -3,14 +3,10 @@ ManaCost:U U
Types:Creature Merfolk Rogue
PT:2/2
T:Mode$ Taps | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ Whenever CARDNAME becomes tapped, exile the top three cards of target opponent's library face down.
SVar:TrigExile:DB$ Dig | ValidTgts$ Opponent | DigNum$ 3 | ChangeNum$ All | DestinationZone$ Exile | ExileFaceDown$ True | RememberChanged$ True
S:Mode$ Continuous | Affected$ Card.IsRemembered+ExiledWithSource | AffectedZone$ Exile | MayLookAt$ You | Description$ You may look at cards exiled with CARDNAME.
A:AB$ SetState | Cost$ U Sac<1/CARDNAME> | Defined$ Remembered | Mode$ TurnFace | SubAbility$ DBCounter | SpellDescription$ Turn all cards exiled with CARDNAME face up. Counter all spells with those names.
SVar:DBCounter:DB$ Counter | AllType$ Spell | AllValid$ Card.sharesNameWith Remembered.ExiledWithSource | SubAbility$ DBCleanup
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:TrigExile:DB$ Dig | ValidTgts$ Opponent | DigNum$ 3 | ChangeNum$ All | DestinationZone$ Exile | ExileFaceDown$ True
S:Mode$ Continuous | Affected$ Card.ExiledWithSource | AffectedZone$ Exile | MayLookAt$ You | Description$ You may look at cards exiled with CARDNAME.
A:AB$ SetState | Cost$ U Sac<1/CARDNAME> | Defined$ ExiledWith | Mode$ TurnFace | SubAbility$ DBCounter | SpellDescription$ Turn all cards exiled with CARDNAME face up. Counter all spells with those names.
SVar:DBCounter:DB$ Counter | AllType$ Spell | AllValid$ Card.sharesNameWith ExiledWith | SubAbility$ DBCleanup
AI:RemoveDeck:All
AI:RemoveDeck:Random
Oracle:Whenever Grimoire Thief becomes tapped, exile the top three cards of target opponent's library face down.\nYou may look at cards exiled with Grimoire Thief.\n{U}, Sacrifice Grimoire Thief: Turn all cards exiled with Grimoire Thief face up. Counter all spells with those names.

View File

@@ -1,16 +1,10 @@
Name:Gustha's Scepter
ManaCost:0
Types:Artifact
A:AB$ ChangeZone | Cost$ T | ChangeType$ Card | ChangeNum$ 1 | Origin$ Hand | Destination$ Exile | ExileFaceDown$ True | RememberChanged$ True | Mandatory$ True | SubAbility$ DBEffect | SpellDescription$ Exile a card from your hand face down. You may look at it for as long as it remains exiled.
SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STLook | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup
SVar:STLook:Mode$ Continuous | MayLookAt$ You | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may look at it for as long as it remains exiled.
A:AB$ ChooseCard | Cost$ T | Defined$ You | Amount$ 1 | Mandatory$ True | AILogic$ AtLeast1 | ChoiceTitle$ Choose a card you own to put into your hand | Choices$ Card.IsRemembered+YouOwn+ExiledWithSource | ChoiceZone$ Exile | SubAbility$ MoveChosen | SpellDescription$ Return a card you own exiled with CARDNAME to your hand.
SVar:MoveChosen:DB$ ChangeZone | Origin$ Exile | Destination$ Hand | Defined$ ChosenCard
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ DBChangeZoneAll | TriggerDescription$ When you lose control of CARDNAME, put all cards exiled with CARDNAME into their owner's graveyard.
T:Mode$ ChangesController | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ DBChangeZoneAll | Secondary$ True | TriggerDescription$ When you lose control of CARDNAME, put all cards exiled with CARDNAME into their owner's graveyard.
SVar:DBChangeZoneAll:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ Exile | Destination$ Graveyard | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
A:AB$ ChangeZone | Cost$ T | ChangeType$ Card | ChangeNum$ 1 | Origin$ Hand | Destination$ Exile | ExileFaceDown$ True | ExilePeek$ True | Mandatory$ True | SpellDescription$ Exile a card from your hand face down. You may look at it for as long as it remains exiled.
A:AB$ ChangeZone | Cost$ T | Hidden$ True | Origin$ Exile | Destination$ Hand | ChangeType$ Creature.ExiledWithSource+YouOwn | ChangeNum$ 1 | Mandatory$ True | StackDescription$ SpellDescription | SpellDescription$ Return a card you own exiled with CARDNAME to your hand.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ DBChangeZone | TriggerDescription$ When you lose control of CARDNAME, put all cards exiled with CARDNAME into their owner's graveyard.
T:Mode$ ChangesController | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ DBChangeZone | Secondary$ True | TriggerDescription$ When you lose control of CARDNAME, put all cards exiled with CARDNAME into their owner's graveyard.
SVar:DBChangeZone:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Graveyard
AI:RemoveDeck:All
Oracle:{T}: Exile a card from your hand face down. You may look at it for as long as it remains exiled.\n{T}: Return a card you own exiled with Gustha's Scepter to your hand.\nWhen you lose control of Gustha's Scepter, put all cards exiled with Gustha's Scepter into their owner's graveyard.

View File

@@ -2,12 +2,7 @@ Name:Hedonist's Trove
ManaCost:5 B B
Types:Enchantment
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile all cards from target opponent's graveyard.
SVar:TrigExile:DB$ ChangeZoneAll | ValidTgts$ Opponent | TgtPrompt$ Select target Opponent | Origin$ Graveyard | Destination$ Exile | ChangeType$ Card | IsCurse$ True | RememberChanged$ True
S:Mode$ Continuous | MayPlay$ True | Affected$ Land.IsRemembered+ExiledWithSource | AffectedZone$ Exile | Description$ You may play lands from among cards exiled with CARDNAME.
S:Mode$ Continuous | MayPlay$ True | MayPlayLimit$ 1 | Affected$ Card.nonLand+IsRemembered+ExiledWithSource | AffectedZone$ Exile | Description$ You may play cards exiled with CARDNAME.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | Defined$ TriggeredCard | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/hedonists_trove.jpg
SVar:TrigExile:DB$ ChangeZoneAll | ValidTgts$ Opponent | TgtPrompt$ Select target Opponent | Origin$ Graveyard | Destination$ Exile | ChangeType$ Card | IsCurse$ True
S:Mode$ Continuous | MayPlay$ True | Affected$ Land.ExiledWithSource | AffectedZone$ Exile | Description$ You may play lands from among cards exiled with CARDNAME.
S:Mode$ Continuous | MayPlay$ True | MayPlayLimit$ 1 | Affected$ Card.nonLand+ExiledWithSource | AffectedZone$ Exile | Description$ You may cast spells from among cards exiled with CARDNAME. You can't cast more than one spell this way each turn.
Oracle:When Hedonist's Trove enters the battlefield, exile all cards from target opponent's graveyard.\nYou may play lands from among cards exiled with Hedonist's Trove.\nYou may cast spells from among cards exiled with Hedonist's Trove. You can't cast more than one spell this way each turn.

View File

@@ -1,14 +1,9 @@
Name:Helvault
ManaCost:3
Types:Legendary Artifact
A:AB$ ChangeZone | Cost$ 1 T | ValidTgts$ Creature.YouCtrl | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you control | RememberTargets$ True | SpellDescription$ Exile target creature you control.
A:AB$ ChangeZone | Cost$ 7 T | ValidTgts$ Creature.YouDontCtrl | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you don't control | RememberTargets$ True | IsCurse$ True | SpellDescription$ Exile target creature you don't control.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
A:AB$ ChangeZone | Cost$ 1 T | ValidTgts$ Creature.YouCtrl | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you control | SpellDescription$ Exile target creature you control.
A:AB$ ChangeZone | Cost$ 7 T | ValidTgts$ Creature.YouDontCtrl | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you don't control | IsCurse$ True | SpellDescription$ Exile target creature you don't control.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME is put into a graveyard from the battlefield, return all cards exiled with it to the battlefield under their owners' control.
SVar:TrigReturn:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered+ExiledWithSource | Origin$ Exile | Destination$ Battlefield | Destination$ Battlefield | SubAbility$ DBCleanup
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ExcludedDestinations$ Graveyard | ValidCard$ Card.Self | Execute$ DBCleanup | Static$ True
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield | Destination$ Battlefield
AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/helvault.jpg
Oracle:{1}, {T}: Exile target creature you control.\n{7}, {T}: Exile target creature you don't control.\nWhen Helvault is put into a graveyard from the battlefield, return all cards exiled with it to the battlefield under their owners' control.

Some files were not shown because too many files have changed in this diff Show More