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

View File

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

View File

@@ -982,7 +982,7 @@ public class ComputerUtilCard {
for(byte c : MagicColor.WUBRG) { for(byte c : MagicColor.WUBRG) {
String devotionCode = "Count$Devotion." + MagicColor.toLongString(c); 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()) { if (devotion > curDevotion && !CardLists.filter(hand, CardPredicates.isColor(c)).isEmpty()) {
curDevotion = devotion; curDevotion = devotion;
chosenColor = MagicColor.toLongString(c); chosenColor = MagicColor.toLongString(c);

View File

@@ -137,8 +137,9 @@ public class ComputerUtilCost {
* the source * the source
* @return true, if successful * @return true, if successful
*/ */
public static boolean checkDiscardCost(final Player ai, final Cost cost, final Card source, SpellAbility sa) { 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; 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 // Exception: when paying generic mana with Cavern of Souls, prefer the colored mana producing ability
// to attempt to make the spell uncounterable when possible. // to attempt to make the spell uncounterable when possible.
if (ComputerUtilAbility.getAbilitySourceName(ma).equals("Cavern of Souls") if (ComputerUtilAbility.getAbilitySourceName(ma).equals("Cavern of Souls") && ma.hasChosenType()
&& sa.getHostCard().getType().getCreatureTypes().contains(ma.getHostCard().getChosenType())) { && sa.getHostCard().getType().getCreatureTypes().contains(ma.getChosenType(0))) {
if (toPay == ManaCostShard.COLORLESS && cost.getUnpaidShards().contains(ManaCostShard.GENERIC)) { 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 // Deprioritize Cavern of Souls, try to pay generic mana with it instead to use the NoCounter ability
continue; continue;
@@ -414,7 +414,6 @@ public class ComputerUtilMana {
// then apply this one // then apply this one
if (!replaceType.isEmpty()) { if (!replaceType.isEmpty()) {
for (SpellAbility saMana : replaceAmount) { for (SpellAbility saMana : replaceAmount) {
Card card = saMana.getHostCard();
if (saMana.hasParam("ReplaceType")) { if (saMana.hasParam("ReplaceType")) {
// replace color and colorless // replace color and colorless
String color = saMana.getParam("ReplaceType"); String color = saMana.getParam("ReplaceType");
@@ -436,8 +435,8 @@ public class ComputerUtilMana {
// replace color // replace color
String color = saMana.getParam("ReplaceColor"); String color = saMana.getParam("ReplaceColor");
if ("Chosen".equals(color)) { if ("Chosen".equals(color)) {
if (card.hasChosenColor()) { if (saMana.hasChosenColor()) {
color = MagicColor.toShortString(card.getChosenColor()); color = MagicColor.toShortString(saMana.getChosenColor());
} }
} }
if (saMana.hasParam("ReplaceOnly")) { if (saMana.hasParam("ReplaceOnly")) {
@@ -489,7 +488,7 @@ public class ComputerUtilMana {
int pAmount = AbilityUtils.calculateAmount(trSA.getHostCard(), trSA.getParamOrDefault("Amount", "1"), trSA); int pAmount = AbilityUtils.calculateAmount(trSA.getHostCard(), trSA.getParamOrDefault("Amount", "1"), trSA);
String produced = trSA.getParam("Produced"); String produced = trSA.getParam("Produced");
if (produced.equals("Chosen")) { if (produced.equals("Chosen")) {
produced = MagicColor.toShortString(trSA.getHostCard().getChosenColor()); produced = MagicColor.toShortString(trSA.getHostCard().getChosenColor(trSA));
} }
manaProduced += " " + StringUtils.repeat(produced, pAmount); manaProduced += " " + StringUtils.repeat(produced, pAmount);
} else if (ApiType.ManaReflected.equals(trSA.getApi())) { } 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, Integer> markedDamage = new HashMap<>();
private final Map<Card, List<String>> cardToChosenClrs = new HashMap<>(); private final Map<Card, List<String>> cardToChosenClrs = new HashMap<>();
private final Map<Card, CardCollection> cardToChosenCards = new HashMap<>(); private final Map<Card, CardCollection> cardToChosenCards = new HashMap<>();
private final Map<Card, String> cardToChosenType = new HashMap<>(); private final Map<Card, List<String>> cardToChosenTypes = new HashMap<>();
private final Map<Card, String> cardToChosenType2 = new HashMap<>();
private final Map<Card, List<String>> cardToRememberedId = 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>> cardToImprintedId = new HashMap<>();
private final Map<Card, List<String>> cardToMergedCards = 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()); newText.append("|Damage:").append(c.getDamage());
} }
if (!c.getChosenColor().isEmpty()) { SpellAbility first = c.getFirstSpellAbility();
newText.append("|ChosenColor:").append(TextUtil.join(c.getChosenColors(), ",")); if (first != null) {
} if (first.hasChosenColor()) {
if (!c.getChosenType().isEmpty()) { newText.append("|ChosenColor:").append(TextUtil.join(first.getChosenColors(), ","));
newText.append("|ChosenType:").append(c.getChosenType()); }
} if (first.hasChosenType()) {
if (!c.getChosenType2().isEmpty()) { newText.append("|ChosenType:").append(TextUtil.join(first.getChosenType(), ","));
newText.append("|ChosenType2:").append(c.getChosenType2()); }
} }
if (!c.getNamedCard().isEmpty()) { if (!c.getNamedCard().isEmpty()) {
newText.append("|NamedCard:").append(c.getNamedCard()); newText.append("|NamedCard:").append(c.getNamedCard());
} }
@@ -638,8 +638,7 @@ public abstract class GameState {
markedDamage.clear(); markedDamage.clear();
cardToChosenClrs.clear(); cardToChosenClrs.clear();
cardToChosenCards.clear(); cardToChosenCards.clear();
cardToChosenType.clear(); cardToChosenTypes.clear();
cardToChosenType2.clear();
cardToMergedCards.clear(); cardToMergedCards.clear();
cardToScript.clear(); cardToScript.clear();
cardAttackMap.clear(); cardAttackMap.clear();
@@ -734,7 +733,7 @@ public abstract class GameState {
if (persistent) { if (persistent) {
produced.put("PersistentMana", "True"); produced.put("PersistentMana", "True");
} }
final AbilityManaPart abMana = new AbilityManaPart(dummy, produced); final AbilityManaPart abMana = new AbilityManaPart(dummy, null, produced);
game.getAction().invoke(new Runnable() { game.getAction().invoke(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -829,6 +828,7 @@ public abstract class GameState {
Card exiledWith = idToCard.get(Integer.parseInt(id)); Card exiledWith = idToCard.get(Integer.parseInt(id));
c.setExiledWith(exiledWith); c.setExiledWith(exiledWith);
c.setExiledBy(exiledWith.getController()); c.setExiledBy(exiledWith.getController());
exiledWith.addExiledWith(c);
} }
} }
@@ -1087,19 +1087,13 @@ public abstract class GameState {
Card c = entry.getKey(); Card c = entry.getKey();
List<String> colors = entry.getValue(); List<String> colors = entry.getValue();
c.setChosenColors(colors); c.setChosenColors(colors, c.getFirstSpellAbility());
} }
// Chosen type // Chosen type
for (Entry<Card, String> entry : cardToChosenType.entrySet()) { for (Entry<Card, List<String>> entry : cardToChosenTypes.entrySet()) {
Card c = entry.getKey(); Card c = entry.getKey();
c.setChosenType(entry.getValue()); c.setChosenType(entry.getValue(), c.getFirstSpellAbility());
}
// Chosen type 2
for (Entry<Card, String> entry : cardToChosenType2.entrySet()) {
Card c = entry.getKey();
c.setChosenType2(entry.getValue());
} }
// Named card // Named card
@@ -1385,9 +1379,7 @@ public abstract class GameState {
} else if (info.startsWith("ChosenColor:")) { } else if (info.startsWith("ChosenColor:")) {
cardToChosenClrs.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(","))); cardToChosenClrs.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(",")));
} else if (info.startsWith("ChosenType:")) { } else if (info.startsWith("ChosenType:")) {
cardToChosenType.put(c, info.substring(info.indexOf(':') + 1)); cardToChosenTypes.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(",")));
} else if (info.startsWith("ChosenType2:")) {
cardToChosenType2.put(c, info.substring(info.indexOf(':') + 1));
} else if (info.startsWith("ChosenCards:")) { } else if (info.startsWith("ChosenCards:")) {
CardCollection chosen = new CardCollection(); CardCollection chosen = new CardCollection();
String[] idlist = info.substring(info.indexOf(':') + 1).split(","); 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.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
@@ -80,7 +79,6 @@ import forge.util.MyRandom;
import forge.util.collect.FCollection; import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
/** /**
* A prototype for player controller class * A prototype for player controller class
* *
@@ -545,13 +543,12 @@ public class PlayerControllerAi extends PlayerController {
} }
@Override @Override
public String chooseSomeType(String kindOfType, SpellAbility sa, Collection<String> validTypes, List<String> invalidTypes, boolean isOptional) { public List<String> chooseSomeType(String kindOfType, SpellAbility sa, int min, int max, List<String> validTypes) {
String chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa.getParam("AILogic"), validTypes, invalidTypes); List<String> chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa.getParam("AILogic"), min, max, validTypes);
if (StringUtils.isBlank(chosen) && !validTypes.isEmpty()) { if (chosen.isEmpty()) {
chosen = validTypes.iterator().next(); return validTypes.subList(0, min);
System.err.println("AI has no idea how to choose " + kindOfType +", defaulting to arbitrary element: chosen");
} }
getGame().getAction().notifyOfValue(sa, player, chosen, player); getGame().getAction().notifyOfValue(sa, player, chosen.toString(), player);
return chosen; return chosen;
} }

View File

@@ -1000,7 +1000,7 @@ public class SpecialCardAi {
return false; return false;
} }
String prominentColor = ComputerUtilCard.getMostProminentColor(ai.getCardsIn(ZoneType.Battlefield)); 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); 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) // 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 // allow ChosenType - overrides anything else specified
if (types.hasSubtype("ChosenType")) { if (types.hasSubtype("ChosenType")) {
types.clear(); types.clear();
types.add(source.getChosenType()); types.addAll(sa.getChosenType());
} }
final List<String> keywords = Lists.newArrayList(); final List<String> keywords = Lists.newArrayList();
@@ -432,7 +432,7 @@ public class AnimateAi extends SpellAbilityAi {
if (sa.hasParam("Colors")) { if (sa.hasParam("Colors")) {
final String colors = sa.getParam("Colors"); final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) { if (colors.equals("ChosenColor")) {
tmpDesc = CardUtil.getShortColorsString(source.getChosenColors()); tmpDesc = CardUtil.getShortColorsString(sa.getChosenColors());
} else { } else {
tmpDesc = CardUtil.getShortColorsString(Lists.newArrayList(Arrays.asList(colors.split(",")))); tmpDesc = CardUtil.getShortColorsString(Lists.newArrayList(Arrays.asList(colors.split(","))));
} }

View File

@@ -74,9 +74,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
@Override @Override
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) { 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 // currently used by SacAndUpgrade logic, might need simplification
sa.getHostCard().removeSVar("AIPreferenceOverride"); host.removeSVar("AIPreferenceOverride");
} }
if (aiLogic.equals("BeforeCombat")) { if (aiLogic.equals("BeforeCombat")) {
@@ -90,7 +92,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} else if (aiLogic.equals("PriorityOptionalCost")) { } else if (aiLogic.equals("PriorityOptionalCost")) {
boolean highPriority = false; 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 // 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 // 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) highPriority |= ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ai.getGame().getCombat() != null && ComputerUtilCombat.lifeInDanger(ai, ai.getGame().getCombat()); && ai.getGame().getCombat() != null && ComputerUtilCombat.lifeInDanger(ai, ai.getGame().getCombat());
@@ -125,6 +127,40 @@ public class ChangeZoneAi extends SpellAbilityAi {
return true; return true;
} else if (aiLogic.equals("Pongify")) { } else if (aiLogic.equals("Pongify")) {
return SpecialAiLogic.doPongifyLogic(ai, sa); 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); return super.checkAiLogic(ai, sa, aiLogic);
@@ -161,8 +197,15 @@ public class ChangeZoneAi extends SpellAbilityAi {
return SpecialCardAi.MazesEnd.consider(aiPlayer, sa); return SpecialCardAi.MazesEnd.consider(aiPlayer, sa);
} else if (aiLogic.equals("Pongify")) { } else if (aiLogic.equals("Pongify")) {
return sa.isTargetNumberValid(); // Pre-targeted in checkAiLogic 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)) { if (isHidden(sa)) {
return hiddenOriginCanPlayAI(aiPlayer, sa); return hiddenOriginCanPlayAI(aiPlayer, sa);
} }

View File

@@ -245,25 +245,8 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
&& !ComputerUtil.isPlayingReanimator(ai); && !ComputerUtil.isPlayingReanimator(ai);
} }
} else if (origin.equals(ZoneType.Exile)) { } else if (origin.equals(ZoneType.Exile)) {
String logic = sa.getParam("AILogic"); // TODO: nothing to do here at the moment
return false;
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());
}
} else if (origin.equals(ZoneType.Stack)) { } else if (origin.equals(ZoneType.Stack)) {
// time stop can do something like this: // time stop can do something like this:
// Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip // 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.CardLists;
import forge.game.card.CardPredicates; import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets; import forge.game.card.CardPredicates.Presets;
import forge.game.card.CounterEnumType;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.keyword.Keyword; import forge.game.keyword.Keyword;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
@@ -97,18 +96,6 @@ public class ChooseCardAi extends SpellAbilityAi {
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref; 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(); return !choices.isEmpty();
} else if (aiLogic.equals("RandomNonLand")) { } else if (aiLogic.equals("RandomNonLand")) {
return !CardLists.getValidCards(choices, "Card.nonLand", host.getController(), host, sa).isEmpty(); 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.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import forge.ai.ComputerUtilAbility; import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost; import forge.ai.ComputerUtilCost;
@@ -31,6 +32,7 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.AbilitySub; import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbilityCantBeCast;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.Aggregates; import forge.util.Aggregates;
import forge.util.collect.FCollection; import forge.util.collect.FCollection;
@@ -228,11 +230,10 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
return allow; return allow;
} }
//if Iona does prevent from casting, allow it to draw SpellAbility firstSpell = imprinted.getFirstSpellAbility();
for (final Card io : player.getCardsIn(ZoneType.Battlefield, "Iona, Shield of Emeria")) { // check if something would prevent it from casting
if (CardUtil.getColors(imprinted).hasAnyColor(MagicColor.fromName(io.getChosenColor()))) { if (firstSpell == null || StaticAbilityCantBeCast.cantBeCastAbility(firstSpell, imprinted, owner)) {
return allow; return allow;
}
} }
if (dmg == 0) { if (dmg == 0) {

View File

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

View File

@@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@@ -22,8 +23,10 @@ import forge.game.card.CardView;
import forge.game.card.IHasCardView; import forge.game.card.IHasCardView;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.Expressions; import forge.util.Expressions;
import forge.util.collect.FCollection;
/** /**
* Base class for Triggers,ReplacementEffects and StaticAbilities. * Base class for Triggers,ReplacementEffects and StaticAbilities.
@@ -34,6 +37,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
/** The host card. */ /** The host card. */
protected Card hostCard; protected Card hostCard;
protected CardState cardState = null; protected CardState cardState = null;
private StaticAbility grantorStatic = null;
/** The map params. */ /** The map params. */
protected Map<String, String> originalMapParams = Maps.newHashMap(), protected Map<String, String> originalMapParams = Maps.newHashMap(),
@@ -487,7 +491,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
protected IHasSVars getSVarFallback() { protected IHasSVars getSVarFallback() {
if (getCardState() != null) if (getCardState() != null)
return getCardState(); return getCardState();
return getHostCard(); return getOriginalOrHost();
} }
@Override @Override
@@ -501,7 +505,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
@Override @Override
public boolean hasSVar(final String name) { 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) { public Integer getSVarInt(final String name) {
@@ -564,6 +568,19 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
return !getHostCard().equals(getCardState().getCard()); 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() { public Map<String, String> getChangedTextColors() {
return _combineChangedMap(intrinsicChangedTextColors, changedTextColors); 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 // dont use setHostCard to not trigger the not copied parts yet
copy.hostCard = host; 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")) { } else if (property.startsWith("ChosenColor")) {
if (property.endsWith("Source") && isColorlessSource) if (property.endsWith("Source") && isColorlessSource)
return false; 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")) { } else if (property.startsWith("AnyChosenColor")) {
if (property.endsWith("Source") && isColorlessSource) if (property.endsWith("Source") && isColorlessSource)
return false; return false;
return source.hasChosenColor() return spellAbility.hasChosenColor()
&& colors.hasAnyColor(ColorSet.fromNames(source.getChosenColors()).getColor()); && colors.hasAnyColor(ColorSet.fromNames(spellAbility.getChosenColors()).getColor());
} else if (property.startsWith("non")) { } else if (property.startsWith("non")) {
// ... Other Card types // ... Other Card types
@@ -71,13 +71,15 @@ public class ForgeScript {
final String svar = property.substring(8); final String svar = property.substring(8);
return cardState.hasSVar(svar); return cardState.hasSVar(svar);
} else if (property.equals("ChosenType")) { } 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")) { } else if (property.equals("IsNotChosenType")) {
return !cardState.getTypeWithChanges().hasStringType(source.getChosenType()); if (spellAbility.hasChosenType() && cardState.getTypeWithChanges().hasStringType(spellAbility.getChosenType(0))) {
} else if (property.equals("ChosenType2")) { return false;
return cardState.getTypeWithChanges().hasStringType(source.getChosenType2()); }
} else if (property.equals("IsNotChosenType2")) { return true;
return !cardState.getTypeWithChanges().hasStringType(source.getChosenType2());
} else if (property.startsWith("HasSubtype")) { } else if (property.startsWith("HasSubtype")) {
final String subType = property.substring(11); final String subType = property.substring(11);
return cardState.getTypeWithChanges().hasSubtype(subType); return cardState.getTypeWithChanges().hasSubtype(subType);

View File

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

View File

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

View File

@@ -19,7 +19,7 @@ public class AbilityApiBased extends AbilityActivated {
effect = api.getSpellEffect(); effect = api.getSpellEffect();
if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) { 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 this.setUndoable(true); // will try at least
} }

View File

@@ -125,6 +125,10 @@ public class AbilityUtils {
c = AbilityUtils.findEffectRoot(hostCard); 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")) { else if (defined.equals("Equipped")) {
c = hostCard.getEquipping(); c = hostCard.getEquipping();
} }
@@ -580,6 +584,10 @@ public class AbilityUtils {
return AbilityUtils.xCount(ability.getOriginalHost(), calcX[1], ability) * multiplier; 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")) { if (calcX[0].startsWith("Remembered")) {
// Add whole Remembered list to handlePaid // Add whole Remembered list to handlePaid
final CardCollection list = new CardCollection(); final CardCollection list = new CardCollection();
@@ -1050,6 +1058,9 @@ public class AbilityUtils {
players.add(s.getActivatingPlayer()); players.add(s.getActivatingPlayer());
} }
} }
else if (defined.startsWith("ExiledWith")) {
addPlayer(Lists.newArrayList(card.getExiledWith(sa)), defined, players);
}
else if (defined.startsWith("Remembered")) { else if (defined.startsWith("Remembered")) {
addPlayer(card.getRemembered(), defined, players); addPlayer(card.getRemembered(), defined, players);
} }
@@ -1725,7 +1736,7 @@ public class AbilityUtils {
if (sq[0].contains("HasNumChosenColors")) { if (sq[0].contains("HasNumChosenColors")) {
int sum = 0; int sum = 0;
for (Card card : AbilityUtils.getDefinedCards(sa.getHostCard(), sq[1], sa)) { 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; return sum;
} }
@@ -1834,6 +1845,33 @@ public class AbilityUtils {
return CardFactoryUtil.doXMath(game.getCounterAddedThisTurn(cType, parts[2], parts[3], c, player, ctb), expr, c); 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); return CardFactoryUtil.xCount(c, s2);
} }

View File

@@ -25,7 +25,7 @@ public class SpellApiBased extends Spell {
this.setIntrinsic(true); this.setIntrinsic(true);
if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) { 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)) { 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 // allow ChosenType - overrides anything else specified
if (types.hasSubtype("ChosenType")) { if (types.hasSubtype("ChosenType")) {
types.clear(); types.clear();
types.add(host.getChosenType()); types.addAll(sa.getChosenType());
} }
final List<String> keywords = new ArrayList<>(); final List<String> keywords = new ArrayList<>();
@@ -89,7 +89,7 @@ public class AnimateAllEffect extends AnimateEffectBase {
if (sa.hasParam("Colors")) { if (sa.hasParam("Colors")) {
final String colors = sa.getParam("Colors"); final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) { if (colors.equals("ChosenColor")) {
tmpDesc = CardUtil.getShortColorsString(host.getChosenColors()); tmpDesc = CardUtil.getShortColorsString(sa.getChosenColors());
} else { } else {
tmpDesc = CardUtil.getShortColorsString(new ArrayList<>(Arrays.asList(colors.split(",")))); 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 // allow ChosenType - overrides anything else specified
if (types.hasSubtype("ChosenType")) { if (types.hasSubtype("ChosenType")) {
types.clear(); types.clear();
types.add(source.getChosenType()); types.addAll(sa.getChosenType());
} }
final List<String> keywords = Lists.newArrayList(); final List<String> keywords = Lists.newArrayList();
@@ -100,7 +100,7 @@ public class AnimateEffect extends AnimateEffectBase {
final String colors = sa.getParam("Colors"); final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) { if (colors.equals("ChosenColor")) {
tmpDesc = CardUtil.getShortColorsString(source.getChosenColors()); tmpDesc = CardUtil.getShortColorsString(sa.getChosenColors());
} else { } else {
tmpDesc = CardUtil.getShortColorsString(Arrays.asList(colors.split(","))); tmpDesc = CardUtil.getShortColorsString(Arrays.asList(colors.split(",")));
} }

View File

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

View File

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

View File

@@ -685,16 +685,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} }
movedCard.setTimestamp(ts); movedCard.setTimestamp(ts);
} else { } 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); movedCard = game.getAction().moveTo(destination, gameCard, cause);
if (ZoneType.Hand.equals(destination) && ZoneType.Command.equals(originZone.getZoneType())) { if (ZoneType.Hand.equals(destination) && ZoneType.Command.equals(originZone.getZoneType())) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -713,10 +703,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
// might set after card is moved again if something has changed // might set after card is moved again if something has changed
if (destination.equals(ZoneType.Exile) && !movedCard.isToken()) { if (destination.equals(ZoneType.Exile) && !movedCard.isToken()) {
movedCard.setExiledWith(host); movedCard.setExiledWith(hostCard);
if (host != null) { hostCard.addExiledWith(movedCard, sa);
movedCard.setExiledBy(host.getController()); movedCard.setExiledBy(sa.getActivatingPlayer());
}
} }
if (sa.hasParam("WithCountersType")) { if (sa.hasParam("WithCountersType")) {
@@ -728,6 +717,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (sa.hasParam("ExileFaceDown") || sa.hasParam("FaceDown")) { if (sa.hasParam("ExileFaceDown") || sa.hasParam("FaceDown")) {
movedCard.turnFaceDown(true); movedCard.turnFaceDown(true);
} }
if (sa.hasParam("ExilePeek")) {
movedCard.addMayLookTemp(player);
}
if (sa.hasParam("Foretold")) { if (sa.hasParam("Foretold")) {
movedCard.setForetold(true); movedCard.setForetold(true);
movedCard.setForetoldThisTurn(true); movedCard.setForetoldThisTurn(true);
@@ -810,7 +802,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
triggerList.triggerChangesZoneAll(game); triggerList.triggerChangesZoneAll(game);
counterTable.triggerCountersPutAll(game); counterTable.triggerCountersPutAll(game);
if (sa.hasParam("AtEOT") && !triggerList.isEmpty()) { if (sa.hasParam("AtEOT") && !triggerList.isEmpty()) {
registerDelayedTrigger(sa, sa.getParam("AtEOT"), triggerList.allCards()); registerDelayedTrigger(sa, sa.getParam("AtEOT"), triggerList.allCards());
} }
@@ -1307,12 +1298,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
else if (destination.equals(ZoneType.Exile)) { else if (destination.equals(ZoneType.Exile)) {
movedCard = game.getAction().exile(c, sa, moveParams); movedCard = game.getAction().exile(c, sa, moveParams);
if (!c.isToken()) { if (!c.isToken()) {
Card host = sa.getOriginalHost(); movedCard.setExiledWith(source);
if (host == null) { source.addExiledWith(movedCard, sa);
host = sa.getHostCard(); movedCard.setExiledBy(sa.getActivatingPlayer());
}
movedCard.setExiledWith(host);
movedCard.setExiledBy(host.getController());
} }
if (sa.hasParam("ExileFaceDown")) { if (sa.hasParam("ExileFaceDown")) {
movedCard.turnFaceDown(true); movedCard.turnFaceDown(true);
@@ -1459,13 +1447,12 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} else if (srcSA.getParam("Destination").equals("Graveyard")) { } else if (srcSA.getParam("Destination").equals("Graveyard")) {
movedCard = game.getAction().moveToGraveyard(tgtHost, srcSA, params); movedCard = game.getAction().moveToGraveyard(tgtHost, srcSA, params);
} else if (srcSA.getParam("Destination").equals("Exile")) { } else if (srcSA.getParam("Destination").equals("Exile")) {
Card host = srcSA.getOriginalHost(); Card hostCard = srcSA.getHostCard();
if (host == null) {
host = srcSA.getHostCard();
}
movedCard = game.getAction().exile(tgtHost, srcSA, params); 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")) { } else if (srcSA.getParam("Destination").equals("TopOfLibrary")) {
movedCard = game.getAction().moveToLibrary(tgtHost, srcSA, params); movedCard = game.getAction().moveToLibrary(tgtHost, srcSA, params);
} else if (srcSA.getParam("Destination").equals("Hand")) { } 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()) { if (chosenColors.isEmpty()) {
return; return;
} }
card.setChosenColors(chosenColors); card.setChosenColors(chosenColors, sa);
p.getGame().getAction().notifyOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), Lang.joinHomogenous(chosenColors)), p); 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; package forge.game.ability.effects;
import java.security.InvalidParameterException; import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import forge.card.CardType; import forge.card.CardType;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import com.google.common.collect.Lists;
public class ChooseTypeEffect extends SpellAbilityEffect { public class ChooseTypeEffect extends SpellAbilityEffect {
@@ -30,14 +31,12 @@ public class ChooseTypeEffect extends SpellAbilityEffect {
public void resolve(SpellAbility sa) { public void resolve(SpellAbility sa) {
final Card card = sa.getHostCard(); final Card card = sa.getHostCard();
final String type = sa.getParam("Type"); 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")) { if (sa.hasParam("ValidTypes")) {
validTypes.addAll(Arrays.asList(sa.getParam("ValidTypes").split(","))); validTypes.addAll(Arrays.asList(sa.getParam("ValidTypes").split(",")));
} } else {
if (validTypes.isEmpty()) {
switch (type) { switch (type) {
case "Card": case "Card":
validTypes.addAll(CardType.getAllCardTypes()); validTypes.addAll(CardType.getAllCardTypes());
@@ -54,22 +53,22 @@ public class ChooseTypeEffect extends SpellAbilityEffect {
} }
} }
for (final String s : invalidTypes) { if (sa.hasParam("InvalidTypes")) {
validTypes.remove(s); 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()) { if (!validTypes.isEmpty()) {
for (final Player p : tgtPlayers) { for (final Player p : getTargetPlayers(sa)) {
if ((tgt == null) || p.canBeTargetedBy(sa)) { if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) {
String choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes); List<String> choices = Lists.newArrayList();
if (!sa.hasParam("Secondary")) { for (int i = 0; i < amount; i++) {
card.setChosenType(choice); // the only one with multiple amount currently cares about the order
} else { choices.addAll(p.getController().chooseSomeType(type, sa, 1, 1, validTypes));
card.setChosenType2(choice); validTypes.removeAll(choices);
} }
card.setChosenType(choices, sa);
} }
} }
} }

View File

@@ -49,11 +49,10 @@ public class CleanUpEffect extends SpellAbilityEffect {
source.setChosenPlayer(null); source.setChosenPlayer(null);
} }
if (sa.hasParam("ClearChosenType")) { if (sa.hasParam("ClearChosenType")) {
source.setChosenType(""); source.setChosenType(null, sa);;
source.setChosenType2("");
} }
if (sa.hasParam("ClearChosenColor")) { 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.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardFactoryUtil; import forge.game.card.CardFactoryUtil;
import forge.game.player.PlayerController.BinaryChoiceType;
import forge.game.replacement.ReplacementResult; import forge.game.replacement.ReplacementResult;
import forge.game.replacement.ReplacementType; import forge.game.replacement.ReplacementType;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -158,7 +159,7 @@ public class CounterEffect extends SpellAbilityEffect {
* <p> * <p>
* removeFromStack. * removeFromStack.
* </p> * </p>
* *
* @param tgtSA * @param tgtSA
* a {@link forge.game.spellability.SpellAbility} object. * a {@link forge.game.spellability.SpellAbility} object.
* @param srcSA * @param srcSA
@@ -170,7 +171,7 @@ public class CounterEffect extends SpellAbilityEffect {
private static void removeFromStack(final SpellAbility tgtSA, private static void removeFromStack(final SpellAbility tgtSA,
final SpellAbility srcSA, final SpellAbilityStackInstance si) { final SpellAbility srcSA, final SpellAbilityStackInstance si) {
final Game game = tgtSA.getActivatingPlayer().getGame(); 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()); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(tgtSA.getHostCard());
repParams.put(AbilityKey.TgtSA, tgtSA); repParams.put(AbilityKey.TgtSA, tgtSA);
repParams.put(AbilityKey.Cause, srcSA.getHostCard()); 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"; String destination = srcSA.hasParam("Destination") ? srcSA.getParam("Destination") : tgtSA.isAftermath() ? "Exile" : "Graveyard";
if (srcSA.hasParam("DestinationChoice")) {//Hinder if (srcSA.hasParam("DestinationChoice")) {//Hinder
List<String> pos = Arrays.asList(srcSA.getParam("DestinationChoice").split(",")); 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()) { if (tgtSA.isAbility()) {
// For Ability-targeted counterspells - do not move it anywhere, // 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.Cause, srcSA.getHostCard());
runParams.put(AbilityKey.CounteredSA, tgtSA); runParams.put(AbilityKey.CounteredSA, tgtSA);
game.getTriggerHandler().runTrigger(TriggerType.Countered, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.Countered, runParams, false);
if (!tgtSA.isAbility()) { if (!tgtSA.isAbility()) {
game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, "Send countered spell to " + destination); 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.TextUtil;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
import org.apache.commons.lang3.StringUtils;
public class DigEffect extends SpellAbilityEffect { public class DigEffect extends SpellAbilityEffect {
@Override @Override
@@ -187,7 +189,7 @@ public class DigEffect extends SpellAbilityEffect {
} }
else if (!changeValid.isEmpty()) { else if (!changeValid.isEmpty()) {
if (changeValid.contains("ChosenType")) { 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); valid = CardLists.getValidCards(top, changeValid.split(","), host.getController(), host, sa);
} }
@@ -296,10 +298,6 @@ public class DigEffect extends SpellAbilityEffect {
} }
Collections.reverse(movedCards); Collections.reverse(movedCards);
Card effectHost = sa.getOriginalHost();
if (effectHost == null) {
effectHost = sa.getHostCard();
}
for (Card c : movedCards) { for (Card c : movedCards) {
final ZoneType origin = c.getZone().getZoneType(); final ZoneType origin = c.getZone().getZoneType();
final PlayerZone zone = c.getOwner().getZone(destZone1); final PlayerZone zone = c.getOwner().getZone(destZone1);
@@ -324,8 +322,9 @@ public class DigEffect extends SpellAbilityEffect {
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")), c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")),
1, player, true, counterTable); 1, player, true, counterTable);
} }
c.setExiledWith(effectHost); c.setExiledWith(host);
c.setExiledBy(effectHost.getController()); host.addExiledWith(c, sa);
c.setExiledBy(sa.getActivatingPlayer());
} }
} }
if (!origin.equals(c.getZone().getZoneType())) { if (!origin.equals(c.getZone().getZoneType())) {
@@ -335,6 +334,11 @@ public class DigEffect extends SpellAbilityEffect {
if (sa.hasParam("ExileFaceDown")) { if (sa.hasParam("ExileFaceDown")) {
c.turnFaceDown(true); c.turnFaceDown(true);
} }
if (sa.hasParam("ExilePeek")) {
c.addMayLookTemp(player);
}
if (sa.hasParam("Imprint")) { if (sa.hasParam("Imprint")) {
host.addImprintedCard(c); host.addImprintedCard(c);
} }
@@ -393,8 +397,9 @@ public class DigEffect extends SpellAbilityEffect {
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")), c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")),
1, player, true, counterTable); 1, player, true, counterTable);
} }
c.setExiledWith(effectHost); c.setExiledWith(host);
c.setExiledBy(effectHost.getController()); host.addExiledWith(c, sa);
c.setExiledBy(host.getController());
} }
} }
} }

View File

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

View File

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

View File

@@ -70,11 +70,11 @@ public class PlayEffect extends SpellAbilityEffect {
amount = AbilityUtils.calculateAmount(source, sa.getParam("Amount"), sa); amount = AbilityUtils.calculateAmount(source, sa.getParam("Amount"), sa);
} }
Player controller = activator;
if (sa.hasParam("Controller")) { 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 tgtCards;
CardCollection showCards = new CardCollection(); CardCollection showCards = new CardCollection();
@@ -118,7 +118,7 @@ public class PlayEffect extends SpellAbilityEffect {
if (sa.hasParam("ChoiceNum")) { if (sa.hasParam("ChoiceNum")) {
final int choicenum = AbilityUtils.calculateAmount(source, sa.getParam("ChoiceNum"), sa); final int choicenum = AbilityUtils.calculateAmount(source, sa.getParam("ChoiceNum"), sa);
tgtCards = new CardCollection( 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 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(); final CardCollection saidNoTo = new CardCollection();
while (tgtCards.size() > saidNoTo.size() && saidNoTo.size() < amount && amount > 0) { 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); Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"), null);
activator.getController().endTempShowCards(); controller.getController().endTempShowCards();
if (tgtCard == null) { if (tgtCard == null) {
return; return;
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -81,7 +81,7 @@ public class SubgameEffect extends SpellAbilityEffect {
List<String> chosenColors; List<String> chosenColors;
SpellAbility cmdColorsa = new SpellAbility.EmptySa(ApiType.ChooseColor, cmd, player); SpellAbility cmdColorsa = new SpellAbility.EmptySa(ApiType.ChooseColor, cmd, player);
chosenColors = player.getController().chooseColors(prompt,cmdColorsa, 1, 1, colorChoices); 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); subgame.getAction().notifyOfValue(cmdColorsa, cmd, Localizer.getInstance().getMessage("lblPlayerPickedChosen", player.getName(), Lang.joinHomogenous(chosenColors)), player);
} }
cmd.setCommander(true); 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 // linked Abilities, if it needs chosen values, but nothing is chosen, no token can be created
if (sa.hasParam("TokenTypes")) { if (sa.hasParam("TokenTypes")) {
if (sa.getParam("TokenTypes").contains("ChosenType") && !host.hasChosenType()) { if (sa.getParam("TokenTypes").contains("ChosenType") && !sa.hasChosenType()) {
return; return;
} }
} }
if (sa.hasParam("TokenColors")) { if (sa.hasParam("TokenColors")) {
if (sa.getParam("TokenColors").contains("ChosenColor") && !host.hasChosenColor()) { if (sa.getParam("TokenColors").contains("ChosenColor") && !sa.hasChosenColor()) {
return; 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 org.apache.commons.lang3.tuple.Pair;
import com.esotericsoftware.minlog.Log; import com.esotericsoftware.minlog.Log;
import com.google.common.base.Optional;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.HashBasedTable; 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 NavigableMap<Long, Player> tempControllers = Maps.newTreeMap();
private String originalText = "", text = ""; private String originalText = "", text = "";
private String chosenType = ""; private LinkedAbilityTable<String> chosenTypesTable = new LinkedAbilityTable<String>();
private String chosenType2 = ""; private LinkedAbilityTable<String> chosenColorsTable = new LinkedAbilityTable<String>();
private List<String> chosenColors;
private String chosenName = ""; private String chosenName = "";
private String chosenName2 = ""; private String chosenName2 = "";
private Integer chosenNumber; private Integer chosenNumber;
@@ -307,6 +307,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private Card exiledWith = null; private Card exiledWith = null;
private Player exiledBy = null; private Player exiledBy = null;
private ExileWithTable exiledWithTable = new ExileWithTable();
private Map<Long, Player> goad = Maps.newTreeMap(); 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 int planeswalkerAbilityActivated = 0;
private final Map<SpellAbility, Integer> numberTurnActivations = Maps.newHashMap(); private final ActivationTable numberTurnActivations = new ActivationTable();
private final Map<SpellAbility, Integer> numberGameActivations = Maps.newHashMap(); private final ActivationTable numberGameActivations = new ActivationTable();
private final Table<SpellAbility, StaticAbility, Integer> numberTurnActivationsStatic = HashBasedTable.create(); private final ChosenModesTable chosenModesTurn = new ChosenModesTable();
private final Table<SpellAbility, StaticAbility, Integer> numberGameActivationsStatic = HashBasedTable.create(); private final ChosenModesTable chosenModesGame = new ChosenModesTable();
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();
// Enumeration for CMC request types // Enumeration for CMC request types
public enum SplitCMCMode { public enum SplitCMCMode {
@@ -1686,55 +1681,39 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
exiledBy = ep; exiledBy = ep;
} }
// used for cards like Belbe's Portal, Conspiracy, Cover of Darkness, etc. public final Iterable<String> getChosenType(CardTraitBase ctb) {
public final String getChosenType() { return chosenTypesTable.get(ctb);
return chosenType;
} }
public final void setChosenType(final String s) { public final String getChosenType(CardTraitBase ctb, int index) {
chosenType = s; return Iterables.get(chosenTypesTable.get(ctb), index, null);
view.updateChosenType(this);
} }
public final boolean hasChosenType() { public final boolean hasChosenType(CardTraitBase ctb) {
return chosenType != null && !chosenType.isEmpty(); return !chosenTypesTable.get(ctb).isEmpty();
} }
// used by card Illusionary Terrain public void setChosenType(final Iterable<String> types, CardTraitBase ctb) {
public final String getChosenType2() { chosenTypesTable.set(types, ctb);
return chosenType2;
} }
public final void setChosenType2(final String s) { public String getChosenColor(CardTraitBase ctb) {
chosenType2 = s; return Iterables.getFirst(chosenColorsTable.get(ctb), null);
view.updateChosenType2(this); }
public final Iterable<String> getChosenColors(CardTraitBase ctb) {
return chosenColorsTable.get(ctb);
} }
public final boolean hasChosenType2() { public final boolean hasChosenColor(CardTraitBase ctb) {
return chosenType2 != null && !chosenType2.isEmpty(); return !chosenColorsTable.get(ctb).isEmpty();
} }
public final String getChosenColor() { public final boolean hasChosenColor(String s, CardTraitBase ctb) {
if (hasChosenColor()) { return chosenColorsTable.contains(s, ctb);
return chosenColors.get(0);
}
return "";
} }
public final Iterable<String> getChosenColors() {
if (chosenColors == null) { public void setChosenColors(final Iterable<String> colors, CardTraitBase ctb) {
return Lists.newArrayList(); chosenColorsTable.set(colors, ctb);
}
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 final Card getChosenCard() { public final Card getChosenCard() {
@@ -6858,20 +6837,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
public void addAbilityActivated(SpellAbility ability) { public void addAbilityActivated(SpellAbility ability) {
SpellAbility original = ability.getOriginalAbility(); numberTurnActivations.add(ability);
if (original == null) { numberGameActivations.add(ability);
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);
}
if (ability.isPwAbility()) { if (ability.isPwAbility()) {
addPlaneswalkerAbilityActivated(); addPlaneswalkerAbilityActivated();
@@ -6879,121 +6846,27 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
public int getAbilityActivatedThisTurn(SpellAbility ability) { public int getAbilityActivatedThisTurn(SpellAbility ability) {
SpellAbility original = ability.getOriginalAbility(); return numberTurnActivations.get(ability);
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;
} }
public int getAbilityActivatedThisGame(SpellAbility ability) { public int getAbilityActivatedThisGame(SpellAbility ability) {
SpellAbility original = ability.getOriginalAbility(); return numberGameActivations.get(ability);
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;
} }
public List<String> getChosenModesTurn(SpellAbility ability) { public List<String> getChosenModesTurn(SpellAbility ability) {
SpellAbility original = null; return chosenModesTurn.get(ability);
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);
} }
public List<String> getChosenModesGame(SpellAbility ability) { public List<String> getChosenModesGame(SpellAbility ability) {
SpellAbility original = null; return chosenModesGame.get(ability);
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);
} }
public void addChosenModes(SpellAbility ability, String mode) { public void addChosenModes(SpellAbility ability, String mode) {
SpellAbility original = null; chosenModesTurn.put(ability, mode);
SpellAbility root = ability.getRootAbility(); chosenModesGame.put(ability, mode);
// 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);
}
} }
public void resetChosenModeTurn() { public void resetChosenModeTurn() {
chosenModesTurn.clear(); chosenModesTurn.clear();
chosenModesTurnStatic.clear();
} }
public int getPlaneswalkerAbilityActivated() { public int getPlaneswalkerAbilityActivated() {
@@ -7007,7 +6880,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
public void resetActivationsPerTurn() { public void resetActivationsPerTurn() {
planeswalkerAbilityActivated = 0; planeswalkerAbilityActivated = 0;
numberTurnActivations.clear(); numberTurnActivations.clear();
numberTurnActivationsStatic.clear();
} }
public void addCanBlockAdditional(int n, long timestamp) { public void addCanBlockAdditional(int n, long timestamp) {
@@ -7077,6 +6949,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return edition.getBorderColor(); return edition.getBorderColor();
} }
public final void addUntilLeavesBattlefield(final Card c) { public final void addUntilLeavesBattlefield(final Card c) {
untilLeavesBattlefield = view.addCard(untilLeavesBattlefield, c, TrackableProperty.UntilLeavesBattlefield); untilLeavesBattlefield = view.addCard(untilLeavesBattlefield, c, TrackableProperty.UntilLeavesBattlefield);
} }
@@ -7092,4 +6965,32 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
public final void clearUntilLeavesBattlefield() { public final void clearUntilLeavesBattlefield() {
untilLeavesBattlefield = view.clearCards(untilLeavesBattlefield, TrackableProperty.UntilLeavesBattlefield); 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> * </p>
* */ * */
private final static Card copySpellHost(final SpellAbility sourceSA, final SpellAbility targetSA, Player controller) { 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 original = targetSA.getHostCard();
final Card c = copyCard(original, true); final Card c = copyCard(original, true);
@@ -138,7 +137,7 @@ public class CardFactory {
String tmp = ""; String tmp = "";
final String newColor = sourceSA.getParam("CopyIsColor"); final String newColor = sourceSA.getParam("CopyIsColor");
if (newColor.equals("ChosenColor")) { if (newColor.equals("ChosenColor")) {
tmp = CardUtil.getShortColorsString(source.getChosenColors()); tmp = CardUtil.getShortColorsString(sourceSA.getChosenColors());
} else { } else {
tmp = CardUtil.getShortColorsString(Lists.newArrayList(newColor.split(","))); tmp = CardUtil.getShortColorsString(Lists.newArrayList(newColor.split(",")));
} }

View File

@@ -1052,28 +1052,6 @@ public class CardFactoryUtil {
} }
return doXMath(colorOcurrencices, m, c); 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")) { if (sq[0].contains("ColorsCtrl")) {
final String restriction = l[0].substring(11); 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."); sb.append(", then put the rest on the bottom of your library.");
final Trigger hideawayTrigger = TriggerHandler.parseTrigger(sb.toString(), card, intrinsic); final Trigger hideawayTrigger = TriggerHandler.parseTrigger(sb.toString(), card, intrinsic);
String hideawayDig = "DB$ Dig | Defined$ You | DigNum$ 4 | DestinationZone$ Exile | ExileFaceDown$ True | RememberChanged$ True"; String hideawayDig = "DB$ Dig | Defined$ You | DigNum$ 4 | DestinationZone$ Exile | ExileFaceDown$ True";
String hideawayEffect = "DB$ Effect | StaticAbilities$ STHideawayEffectLookAtCard | ForgetOnMoved$ Exile | RememberObjects$ Remembered | Duration$ Permanent"; 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."; 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()); gainControlTrigger.setOverridingAbility((AbilitySub)effectSA.copy());
triggers.add(gainControlTrigger); 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) { for (final Trigger trigger : triggers) {
inst.addTrigger(trigger); inst.addTrigger(trigger);
} }

View File

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

View File

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

View File

@@ -2,7 +2,6 @@ package forge.game.card;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -319,20 +318,6 @@ public class CardView extends GameEntityView {
set(TrackableProperty.ShieldCount, c.getShieldCount()); 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() { public String getChosenNumber() {
return get(TrackableProperty.ChosenNumber); return get(TrackableProperty.ChosenNumber);
} }
@@ -340,13 +325,6 @@ public class CardView extends GameEntityView {
set(TrackableProperty.ChosenNumber, c.getChosenNumber().toString()); 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() { public FCollectionView<CardView> getMergedCardsCollection() {
return get(TrackableProperty.MergedCardsCollection); 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.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
@@ -332,7 +331,7 @@ public class TokenInfo {
final Card host = sa.getHostCard(); final Card host = sa.getHostCard();
final Game game = host.getGame(); 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); PaperToken token = StaticData.instance().getAllTokens().getToken(script, edition);
if (token == null) { if (token == null) {
@@ -359,14 +358,16 @@ public class TokenInfo {
// need to be done after text change so it isn't affected by that // need to be done after text change so it isn't affected by that
if (sa.hasParam("TokenTypes")) { if (sa.hasParam("TokenTypes")) {
String types = sa.getParam("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.addType(types);
result.setName(types); result.setName(types);
} }
if (sa.hasParam("TokenColors")) { if (sa.hasParam("TokenColors")) {
String colors = sa.getParam("TokenColors"); String colors = sa.getParam("TokenColors");
colors = colors.replace("ChosenColor", sa.getHostCard().getChosenColor()); colors = colors.replace("ChosenColor", sa.getChosenColor());
result.setColor(MagicColor.toShortString(colors)); result.setColor(MagicColor.toShortString(colors));
} }

View File

@@ -233,7 +233,8 @@ public class CostAdjustment {
sa.getHostCard().addDelved(c); sa.getHostCard().addDelved(c);
final Card d = game.getAction().exile(c, null); final Card d = game.getAction().exile(c, null);
d.setExiledWith(sa.getHostCard()); 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); table.put(ZoneType.Graveyard, d.getZone().getZoneType(), d);
} }
} }

View File

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

View File

@@ -12,7 +12,7 @@ import forge.util.TextUtil;
public class PaymentDecision { public class PaymentDecision {
public int c = 0; public int c = 0;
public String type; public Iterable<String> types = null;
public final CardCollection cards = new CardCollection(); public final CardCollection cards = new CardCollection();
public final List<Mana> mana; public final List<Mana> mana;
@@ -43,9 +43,9 @@ public class PaymentDecision {
cards.add(chosen); cards.add(chosen);
} }
public PaymentDecision(String choice) { public PaymentDecision(Iterable<String> choices) {
this(null, null, null, null, null); this(null, null, null, null, null);
type = choice; types = choices;
} }
public static PaymentDecision card(Card chosen) { 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()); return TextUtil.concatWithSpace("Payment Decision:", TextUtil.addSuffix(String.valueOf(c),","), cards.toString());
} }
public static PaymentDecision type(String choice) { public static PaymentDecision types(Iterable<String> choices) {
return new PaymentDecision(choice); return new PaymentDecision(choices);
} }
public static PaymentDecision players(List<Player> players) { 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.Predicate;
import com.google.common.base.Predicates; 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 com.google.common.collect.Maps;
import forge.card.CardType;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.card.Card; import forge.game.card.Card;
@@ -138,10 +137,9 @@ public class Untap extends Phase {
} }
if (kw.startsWith("OnlyUntapChosen") && !hasChosen) { if (kw.startsWith("OnlyUntapChosen") && !hasChosen) {
List<String> validTypes = Arrays.asList(kw.split(":")[1].split(",")); List<String> validTypes = Arrays.asList(kw.split(":")[1].split(","));
List<String> invalidTypes = Lists.newArrayList(CardType.getAllCardTypes()); SpellAbility emptySA = new SpellAbility.EmptySa(ApiType.ChooseType, null, player);
invalidTypes.removeAll(validTypes); final String chosen = Iterables.getFirst(player.getController().chooseSomeType("Card", emptySA, 1, 1, validTypes), "");
final String chosen = player.getController().chooseSomeType("Card", new SpellAbility.EmptySa(ApiType.ChooseType, null, player), validTypes, invalidTypes); list = CardLists.getType(list, chosen);
list = CardLists.getType(list,chosen);
hasChosen = true; hasChosen = true;
} }
} }

View File

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

View File

@@ -48,9 +48,9 @@ import forge.item.PaperCard;
import forge.util.ITriggerEvent; import forge.util.ITriggerEvent;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
/** /**
* A prototype for player controller class * A prototype for player controller class
* *
* Handles phase skips for now. * Handles phase skips for now.
*/ */
public abstract class PlayerController { public abstract class PlayerController {
@@ -72,6 +72,7 @@ public abstract class PlayerController {
UntapTimeVault, UntapTimeVault,
LeftOrRight, LeftOrRight,
AddOrRemove, AddOrRemove,
BottomOfLibraryOrTopOfLibrary,
} }
protected final GameView gameView; 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 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 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 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) // Specify a target of a spell (Spellskite)
public abstract Pair<SpellAbilityStackInstance, GameObject> chooseTarget(SpellAbility sa, List<Pair<SpellAbilityStackInstance, GameObject>> allTargets); 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 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, 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 <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); 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 List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand);
public abstract Mana chooseManaFromPool(List<Mana> manaChoices); 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 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); 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 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 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 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 boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call);
public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap); 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 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> 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, public abstract CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, String prompt,
Map<String, Object> params); 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, Predicate<ICardFace> cpp, String valid, String message);
public abstract String chooseCardName(SpellAbility sa, List<ICardFace> faces, 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 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); 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 List<Mana> lastManaProduced = Lists.newArrayList();
private transient Card sourceCard; private transient Card sourceCard;
private transient SpellAbility spellAbility;
// Spells paid with this mana spell can't be countered. // Spells paid with this mana spell can't be countered.
@@ -84,8 +85,9 @@ public class AbilityManaPart implements java.io.Serializable {
* @param sourceCard * @param sourceCard
* a {@link forge.game.card.Card} object. * 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.sourceCard = sourceCard;
this.spellAbility = sa;
origProduced = params.containsKey("Produced") ? params.get("Produced") : "1"; origProduced = params.containsKey("Produced") ? params.get("Produced") : "1";
this.manaRestrictions = params.containsKey("RestrictValid") ? params.get("RestrictValid") : ""; 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. * @return a {@link java.lang.String} object.
*/ */
public final String mana() { public final String mana() {
if (this.getOrigProduced().contains("Chosen")) { if (getOrigProduced().contains("Chosen")) {
if (this.getSourceCard() != null && this.getSourceCard().hasChosenColor()) { if (spellAbility == null) {
return MagicColor.toShortString(this.getSourceCard().getChosenColor()); return "";
} else {
return MagicColor.toShortString(spellAbility.getChosenColor());
} }
} }
return this.getOrigProduced(); return this.getOrigProduced();
@@ -499,15 +503,15 @@ public class AbilityManaPart implements java.io.Serializable {
* a {@link java.lang.String} object. * a {@link java.lang.String} object.
* @return a boolean. * @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? // Any mana never means Colorless?
if (isAnyMana() && !s.equals("C")) { if (isAnyMana() && !s.equals("C")) {
return true; return true;
} }
String origProduced = getOrigProduced(); String origProduced = getOrigProduced();
if (origProduced.contains("Chosen") && sourceCard != null ) { if (origProduced.contains("Chosen") && spellAbility != null ) {
if (getSourceCard().hasChosenColor() && MagicColor.toShortString(getSourceCard().getChosenColor()).contains(s)) { if (spellAbility.hasChosenColor() && MagicColor.toShortString(spellAbility.getChosenColor()).contains(s)) {
return true; return true;
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -83,7 +83,14 @@ public class TriggerTapsForMana extends Trigger {
} }
String produced = (String) prod; String produced = (String) prod;
if ("ChosenColor".equals(getParam("Produced"))) { 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; return false;
} }
} else if (!produced.contains(MagicColor.toShortString(this.getParam("Produced")))) { } else if (!produced.contains(MagicColor.toShortString(this.getParam("Produced")))) {

View File

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

View File

@@ -482,7 +482,7 @@ public class GameSimulatorTest extends SimulationTestCase {
Player p = game.getPlayers().get(1); Player p = game.getPlayers().get(1);
Card bear = addCard(bearCardName, p); Card bear = addCard(bearCardName, p);
Card hall = addCard("Hall of Triumph", 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.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
assertEquals(3, bear.getNetToughness()); assertEquals(3, bear.getNetToughness());

View File

@@ -80,7 +80,7 @@ import forge.util.collect.FCollectionView;
/** /**
* Default harmless implementation for tests. * Default harmless implementation for tests.
* Test-specific behaviour can easily be added by mocking (parts of) this class. * 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, * 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). * 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. * 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 @Override
public CardCollection chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, CardCollection validCards, int min, int max) { public CardCollection chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, CardCollection validCards, int min, int max) {
return chooseItems(validCards, min); return chooseItems((CardCollectionView)validCards, min);
} }
@Override @Override
@@ -416,7 +416,7 @@ public class PlayerControllerForTests extends PlayerController {
@Override @Override
public List<SpellAbility> chooseSpellAbilityToPlay() { public List<SpellAbility> chooseSpellAbilityToPlay() {
//TODO: This method has to return the spellability chosen by player //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) { if (playerActions != null) {
CastSpellFromHandAction castSpellFromHand = playerActions.getNextActionIfApplicable(player, getGame(), CastSpellFromHandAction.class); CastSpellFromHandAction castSpellFromHand = playerActions.getNextActionIfApplicable(player, getGame(), CastSpellFromHandAction.class);
if (castSpellFromHand != null) { if (castSpellFromHand != null) {
@@ -470,7 +470,7 @@ public class PlayerControllerForTests extends PlayerController {
public byte chooseColor(String message, SpellAbility sa, ColorSet colors) { public byte chooseColor(String message, SpellAbility sa, ColorSet colors) {
return Iterables.getFirst(colors, MagicColor.WHITE); return Iterables.getFirst(colors, MagicColor.WHITE);
} }
@Override @Override
public byte chooseColorAllowColorless(String message, Card card, ColorSet colors) { public byte chooseColorAllowColorless(String message, Card card, ColorSet colors) {
return Iterables.getFirst(colors, (byte)0); 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())); 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) { private <T> T chooseItem(Iterable<T> items) {
if (items == null) { if (items == null) {
return null; return null;
@@ -497,8 +504,8 @@ public class PlayerControllerForTests extends PlayerController {
} }
@Override @Override
public String chooseSomeType(String kindOfType, SpellAbility sa, Collection<String> validTypes, List<String> invalidTypes, boolean isOptional) { public List<String> chooseSomeType(String kindOfType, SpellAbility sa, int min, int max, List<String> validTypes) {
return chooseItem(validTypes); return chooseItems(validTypes, min);
} }
@Override @Override
@@ -545,7 +552,7 @@ public class PlayerControllerForTests extends PlayerController {
ComputerUtil.playStack(sa, player, getGame()); ComputerUtil.playStack(sa, player, getGame());
} }
} }
private void prepareSingleSa(final Card host, final SpellAbility sa, boolean isMandatory){ private void prepareSingleSa(final Card host, final SpellAbility sa, boolean isMandatory){
if (sa.hasParam("TargetingPlayer")) { if (sa.hasParam("TargetingPlayer")) {
Player targetingPlayer = AbilityUtils.getDefinedPlayers(host, sa.getParam("TargetingPlayer"), sa).get(0); Player targetingPlayer = AbilityUtils.getDefinedPlayers(host, sa.getParam("TargetingPlayer"), sa).get(0);
@@ -577,7 +584,7 @@ public class PlayerControllerForTests extends PlayerController {
} else { } else {
ComputerUtil.playStack(tgtSA, player, getGame()); ComputerUtil.playStack(tgtSA, player, getGame());
} }
} else } else
return false; // didn't play spell return false; // didn't play spell
} }
return true; return true;

View File

@@ -4,10 +4,8 @@ Types:Creature Angel
PT:5/6 PT:5/6
K:Flying 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$ 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 | Origin$ Battlefield,Graveyard | Destination$ Exile
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 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$ Remembered | Origin$ Exile | Destination$ Hand | SubAbility$ DBCleanup SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Hand
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:PlayMain1:TRUE 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. 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 ManaCost:1 U B
Types:Legendary Planeswalker Ashiok Types:Legendary Planeswalker Ashiok
Loyalty:3 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$ 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$ 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. 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: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$ Remembered | Types$ Nightmare | Permanent$ True
SVar:DBAnimate:DB$ Animate | Defined$ ChosenCard | Types$ Nightmare | Permanent$ True | SubAbility$ DBCleanMinus
SVar:DBCleanMinus:DB$ Cleanup | ForgetDefined$ ChosenCard | ClearChosenCard$ True
SVar:X:Count$xPaid 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. 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.
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
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. 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 Types:Enchantment
K:Flash K:Flash
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile target spell. 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. SVar:TrigExile:DB$ ChangeZone | TargetType$ Spell | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Mandatory$ True | Destination$ Exile | IsCurse$ True | TgtPrompt$ Choose target spell
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. 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.
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 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 | Origin$ Exile | Destination$ Hand | Defined$ Remembered | SubAbility$ DBCleanup SVar:TrigBounce:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Hand
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
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. 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 ManaCost:2
Types:Legendary Artifact 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. 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:DBGainLife:DB$ GainLife | LifeAmount$ 5 | SubAbility$ DBUntap | ConditionCheckSVar$ Y | ConditionSVarCompare$ GE5
SVar:DBUntap:DB$ Untap | Defined$ Self | SubAbility$ DBTransform | 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 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:Y:ExiledWith$DifferentCMC
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
AlternateMode:DoubleFaced 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. 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 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. 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: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. 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 Types:Creature Human Rogue
PT:0/3 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. 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 SVar:DBExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | ExileFaceDown$ True | Mandatory$ True
S:Mode$ Continuous | Affected$ Card.IsRemembered+ExiledWithSource | AffectedZone$ Exile | MayLookAt$ You | Description$ You may look at cards exiled with CARDNAME. S:Mode$ Continuous | Affected$ Card.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. 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.
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
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. 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: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:PlayMain1:TRUE
SVar:OblivionRing: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 Types:Creature Vampire Cleric
PT:1/1 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. 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:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | UntilHostLeavesPlay$ True
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
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. 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: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. // 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 SVar:PlayMain1:TRUE
DeckHints:Type$Vampire 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 PT:1/1
K:Haste 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.) 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 SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | 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. 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.
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
DeckNeeds:Color$Red DeckNeeds:Color$Red
SVar:AISkipDiscardCostCheck:TRUE
AI:RemoveDeck:Random 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. 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 PT:3/3
S:Mode$ Continuous | Affected$ Creature.Nightmare | AddPower$ 1 | AddToughness$ 1 | Description$ Nightmare creatures get +1/+1. 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. 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. 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:TrigExile:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ Nightmare
SVar:PlayMain1:TRUE SVar:PlayMain1:TRUE

View File

@@ -2,13 +2,8 @@ Name:Chrome Mox
ManaCost:0 ManaCost:0
Types:Artifact 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. 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 SVar:TrigExile:DB$ ChangeZone | 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. A:AB$ ManaReflected | Cost$ T | Valid$ ExiledWith | 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:NeedsToPlayVar:Z GE1 SVar:NeedsToPlayVar:Z GE1
SVar:Z:Count$ValidHand Card.nonArtifact+nonColorless+nonLand+YouOwn 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 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.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. 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 SVar:ExileTop:DB$ Dig | DigNum$ 1 | ChangeNum$ All | ValidTgts$ Player | TgtPrompt$ Choose a player | DestinationZone$ Exile
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. 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.
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
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. 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 Types:Artifact Creature Shapeshifter
PT:2/2 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. 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 SVar:TrigDig:DB$ Dig | Defined$ You | DigNum$ 4 | DestinationZone$ Exile | ExileFaceDown$ 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. 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$ Imprinted | SubAbility$ DBChangeZone | Mode$ TurnFace SVar:TrigFaceUp:DB$ SetState | Defined$ ExiledWith | Mode$ TurnFace | SubAbility$ DBChangeZone
SVar:DBChangeZone:DB$ ChangeZone | Defined$ Imprinted | Origin$ Exile | Destination$ Battlefield | ConditionDefined$ Imprinted | ConditionPresent$ Creature | GainControl$ True | SubAbility$ DBCleanup SVar:DBChangeZone:DB$ ChangeZone | Defined$ ExiledWith.Creature | Origin$ Exile | Destination$ Battlefield
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
SVar:SacMe:5 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. 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 Name:Cold Storage
ManaCost:4 ManaCost:4
Types:Artifact 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$ ChangeZone | Cost$ 3 | ValidTgts$ Creature.YouCtrl | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you control | 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. 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.
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:NonStackingEffect:True SVar:NonStackingEffect:True
AI:RemoveDeck:All 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. 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 Name:Colfenor's Plans
ManaCost:2 B B ManaCost:2 B B
Types:Enchantment 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. 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 | RememberChanged$ True | ExileFaceDown$ True | NoReveal$ True SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 7 | ChangeNum$ All | DestinationZone$ Exile | 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.
R:Event$ BeginPhase | ActiveZones$ Battlefield | ValidPlayer$ You | Phase$ Draw | Skip$ True | Description$ Skip your draw step. 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. 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 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. 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 ManaCost:2 B
Types:Creature Vampire Assassin Types:Creature Vampire Assassin
PT:2/2 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. 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 | RememberChanged$ True | SpellDescription$ Exile target creature and put a +1/+1 counter on CARDNAME. 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.
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
SVar:DBCounter:DB$PutCounter | CounterType$ P1P1 | CounterNum$ 1 | Defined$ Self 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. 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 ManaCost:4 U U U
Types:Enchantment 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$ 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 | ChangeType$ Creature.YouCtrl | SubAbility$ DBToken
SVar:TrigExile:DB$ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | ForgetOtherRemembered$ 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: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:X:Remembered$Amount 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. 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 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. 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: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. 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 ManaCost:1 W U
Types:Enchantment 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. 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: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 | ChangeType$ Remembered.sameName | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True 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. 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: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. 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 Types:Creature Human Wizard
PT:1/1 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. 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 SVar:TrigExile:DB$ ChangeZone | 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. 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:Remembered$CardManaCost SVar:X:ExiledWith$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
AI:RemoveDeck:All 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. 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 ManaCost:no cost
Types:Land Desert Types:Land Desert
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. 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$ 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$ 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. 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.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget 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.
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.

View File

@@ -2,11 +2,6 @@ Name:Exclusion Ritual
ManaCost:4 W W ManaCost:4 W W
Types:Enchantment 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. 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 SVar:TrigExile:DB$ ChangeZone | 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. S:Mode$ CantBeCast | ValidCard$ Card.sharesNameWith ExiledWith | 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 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.
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.

View File

@@ -3,11 +3,8 @@ ManaCost:2 B B
Types:Creature Nightmare Horror Types:Creature Nightmare Horror
PT:2/3 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$ 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. 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 | TargetMin$ 1 | IsCurse$ True | ValidTgts$ Creature.Other | TgtPrompt$ Choose target creature other than Faceless Butcher. | RememberTargets$ True | ForgetOtherTargets$ True | Origin$ Battlefield | Destination$ Exile SVar:TrigExile:DB$ ChangeZone | IsCurse$ True | ValidTgts$ Creature.Other | TgtPrompt$ Choose target creature other than CARDNAME. | Origin$ Battlefield | Destination$ Exile
SVar:TrigReturn:DB$ChangeZoneAll | ChangeType$ Card.IsRemembered+ExiledWithSource | Origin$ Exile | Destination$ Battlefield | SubAbility$ DBCleanup SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield
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
SVar:PlayMain1:TRUE 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. 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 Types:Creature Nightmare Horror
PT:2/1 PT:2/1
K:Shadow 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$ 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. | Origin$ Battlefield | Destination$ Exile
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 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$ Remembered | Origin$ Exile | Destination$ Battlefield SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield
SVar:PlayMain1:TRUE 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. 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 Types:Creature Human Cleric
PT:1/3 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. 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 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 | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, return the exiled card to the battlefield under its owner's control. 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 | Origin$ Exile | Destination$ Battlefield | Defined$ DirectRemembered SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield
SVar:PlayMain1:TRUE 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. 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. 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:TrigChooseAttackers:DB$ ChooseCard | DefinedCards$ TriggeredAttackers | SubAbility$ DBExile
SVar:TrigChooseBlockers:DB$ ChooseCard | DefinedCards$ TriggeredBlockers | SubAbility$ DBExile SVar:TrigChooseBlockers:DB$ ChooseCard | DefinedCards$ TriggeredBlockers | SubAbility$ DBExile
SVar:DBExile:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True SVar:DBExile:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Exile
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. 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.
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
K:Equip:3 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} 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 Types:Creature Nightmare Horror
PT:2/2 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$ 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
SVar:TrigExile:DB$ChangeZone | TargetMin$ 0 | TargetMax$ 2 | TargetsFromSingleZone$ True | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card | RememberTargets$ True | ForgetOtherTargets$ True 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$ Remembered | Origin$ Exile | Destination$ Graveyard SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Graveyard
SVar:Picture:http://www.wizards.com/global/images/magic/general/gravegouger.jpg
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. 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 Types:Creature Merfolk Rogue
PT:2/2 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. 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 SVar:TrigExile:DB$ Dig | ValidTgts$ Opponent | DigNum$ 3 | ChangeNum$ All | DestinationZone$ Exile | ExileFaceDown$ True
S:Mode$ Continuous | Affected$ Card.IsRemembered+ExiledWithSource | AffectedZone$ Exile | MayLookAt$ You | Description$ You may look at cards exiled with CARDNAME. 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$ Remembered | Mode$ TurnFace | SubAbility$ DBCounter | SpellDescription$ Turn all cards exiled with CARDNAME face up. Counter all spells with those names. 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 Remembered.ExiledWithSource | SubAbility$ DBCleanup SVar:DBCounter:DB$ Counter | AllType$ Spell | AllValid$ Card.sharesNameWith ExiledWith | 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
AI:RemoveDeck:All AI:RemoveDeck:All
AI:RemoveDeck:Random 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. 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 Name:Gustha's Scepter
ManaCost:0 ManaCost:0
Types:Artifact 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. 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.
SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STLook | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup 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.
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. 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.
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. 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:MoveChosen:DB$ ChangeZone | Origin$ Exile | Destination$ Hand | Defined$ ChosenCard SVar:DBChangeZone:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Graveyard
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
AI:RemoveDeck:All 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. 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 ManaCost:5 B B
Types:Enchantment 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. 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 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.IsRemembered+ExiledWithSource | AffectedZone$ Exile | Description$ You may play lands from among cards exiled with CARDNAME. 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+IsRemembered+ExiledWithSource | AffectedZone$ Exile | Description$ You may play 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.
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
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. 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 Name:Helvault
ManaCost:3 ManaCost:3
Types:Legendary Artifact 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$ 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 | RememberTargets$ True | IsCurse$ True | SpellDescription$ Exile target creature you don't 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$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered+ExiledWithSource | Execute$ DBForget
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
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. 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 SVar:TrigReturn:DB$ ChangeZone | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield | Destination$ Battlefield
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ExcludedDestinations$ Graveyard | ValidCard$ Card.Self | Execute$ DBCleanup | Static$ True
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
AI:RemoveDeck:All 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. 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