This commit is contained in:
tehdiplomat
2019-01-25 08:53:40 -05:00
41 changed files with 495 additions and 104 deletions

View File

@@ -102,6 +102,7 @@ public class DeckRecognizer {
// Pattern.compile("(.*)[^A-Za-wyz]*\\s+([\\d]{1,2})");
private static final Pattern SEARCH_NUMBERS_IN_FRONT = Pattern.compile("([\\d]{1,2})[^A-Za-wyz]*\\s+(.*)");
//private static final Pattern READ_SEPARATED_EDITION = Pattern.compile("[[\\(\\{]([a-zA-Z0-9]){1,3})[]*\\s+(.*)");
private static final Pattern SEARCH_SINGLE_SLASH = Pattern.compile("(?<=[^/])\\s*/\\s*(?=[^/])");
private final SetPreference useLastSet;
private final ICardDatabase db;
@@ -125,7 +126,10 @@ public class DeckRecognizer {
return new Token(TokenType.Comment, 0, rawLine);
}
final char smartQuote = (char) 8217;
final String line = rawLine.trim().replace(smartQuote, '\'');
String line = rawLine.trim().replace(smartQuote, '\'');
// Some websites export split card names with a single slash. Replace with double slash.
line = SEARCH_SINGLE_SLASH.matcher(line).replaceFirst(" // ");
Token result = null;
final Matcher foundNumbersInFront = DeckRecognizer.SEARCH_NUMBERS_IN_FRONT.matcher(line);

View File

@@ -29,6 +29,7 @@ import forge.game.ability.effects.AttachEffect;
import forge.game.card.*;
import forge.game.event.*;
import forge.game.keyword.KeywordInterface;
import forge.game.keyword.KeywordsChange;
import forge.game.player.GameLossReason;
import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
@@ -292,6 +293,33 @@ public class GameAction {
copied.getOwner().addInboundToken(copied);
}
if (toBattlefield) {
// HACK for making the RIOT enchantment look into the Future
// need to check the Keywords what it would have on the Battlefield
Card riotLKI = CardUtil.getLKICopy(copied);
riotLKI.setLastKnownZone(zoneTo);
CardCollection preList = new CardCollection(riotLKI);
checkStaticAbilities(false, Sets.newHashSet(riotLKI), preList);
List<Long> changedTimeStamps = Lists.newArrayList();
for(Map.Entry<Long, KeywordsChange> e : riotLKI.getChangedCardKeywords().entrySet()) {
if (!copied.hasChangedCardKeywords(e.getKey())) {
KeywordsChange o = e.getValue();
o.setHostCard(copied);
for (KeywordInterface k : o.getKeywords()) {
for (ReplacementEffect re : k.getReplacements()) {
// this param need to be set, otherwise in ReplaceMoved it fails
re.getMapParams().put("BypassEtbCheck", "True");
}
}
copied.addChangedCardKeywordsInternal(o, e.getKey());
changedTimeStamps.add(e.getKey());
}
}
checkStaticAbilities(false);
}
Map<String, Object> repParams = Maps.newHashMap();
repParams.put("Event", "Moved");
repParams.put("Affected", copied);

View File

@@ -1465,7 +1465,8 @@ public class Card extends GameEntity implements Comparable<Card> {
}
}
}
if (keyword.startsWith("CantBeCounteredBy")) {
if (keyword.startsWith("CantBeCounteredBy") || keyword.startsWith("Panharmonicon")
|| keyword.startsWith("Dieharmonicon")) {
final String[] p = keyword.split(":");
sbLong.append(p[2]).append("\r\n");
} else if (keyword.startsWith("etbCounter")) {
@@ -3404,6 +3405,15 @@ public class Card extends GameEntity implements Comparable<Card> {
return change;
}
public final boolean hasChangedCardKeywords(final long timestamp) {
return changedCardKeywords.containsKey(timestamp);
}
public final void addChangedCardKeywordsInternal(final KeywordsChange change, final long timestamp) {
changedCardKeywords.put(timestamp, change);
updateKeywordsCache(currentState);
}
// Hidden keywords will be left out
public final Collection<KeywordInterface> getUnhiddenKeywords() {
return getUnhiddenKeywords(currentState);
@@ -5731,7 +5741,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public void setChangedCardKeywords(Map<Long, KeywordsChange> changedCardKeywords) {
this.changedCardKeywords.clear();
for (Entry<Long, KeywordsChange> entry : changedCardKeywords.entrySet()) {
this.changedCardKeywords.put(entry.getKey(), entry.getValue());
this.changedCardKeywords.put(entry.getKey(), entry.getValue().copy(this, true));
}
}

View File

@@ -11,6 +11,7 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import forge.game.Game;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
@@ -45,4 +46,39 @@ public class CardZoneTable extends ForwardingTable<ZoneType, ZoneType, CardColle
game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false);
}
}
public CardCollection filterCards(Iterable<ZoneType> origin, ZoneType destination, String valid, Card host, SpellAbility sa) {
CardCollection allCards = new CardCollection();
if (destination != null) {
if (!containsColumn(destination)) {
return allCards;
}
}
if (origin != null) {
for (ZoneType z : origin) {
if (containsRow(z)) {
if (destination != null) {
allCards.addAll(row(z).get(destination));
} else {
for (CardCollection c : row(z).values()) {
allCards.addAll(c);
}
}
}
}
} else if (destination != null) {
for (CardCollection c : column(destination).values()) {
allCards.addAll(c);
}
} else {
for (CardCollection c : values()) {
allCards.addAll(c);
}
}
if (valid != null) {
allCards = CardLists.getValidCards(allCards, valid.split(","), host.getController(), host, sa);
}
return allCards;
}
}

View File

@@ -8,6 +8,8 @@ import java.util.Iterator;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import forge.game.card.Card;
public class KeywordCollection implements Iterable<String>, Serializable {
private static final long serialVersionUID = -2882986558147844702L;
@@ -151,6 +153,12 @@ public class KeywordCollection implements Iterable<String>, Serializable {
return map.get(keyword);
}
public void setHostCard(final Card host) {
for (KeywordInterface k : map.values()) {
k.setHostCard(host);
}
}
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {

View File

@@ -202,7 +202,7 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
public Collection<StaticAbility> getStaticAbilities() {
return staticAbilities;
}
/*
* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#copy()
@@ -233,7 +233,7 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
return result;
} catch (final Exception ex) {
throw new RuntimeException("KeywordInstance : clone() error, " + ex);
throw new RuntimeException("KeywordInstance : clone() error", ex);
}
}
@@ -252,4 +252,26 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
public boolean redundant(Collection<KeywordInterface> list) {
return !list.isEmpty() && keyword.isMultipleRedundant;
}
/* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#setHostCard(forge.game.card.Card)
*/
@Override
public void setHostCard(Card host) {
for (SpellAbility sa : this.abilities) {
sa.setHostCard(host);
}
for (Trigger tr : this.triggers) {
tr.setHostCard(host);
}
for (ReplacementEffect re : this.replacements) {
re.setHostCard(host);
}
for (StaticAbility sa : this.staticAbilities) {
sa.setHostCard(host);
}
}
}

View File

@@ -31,6 +31,7 @@ public interface KeywordInterface extends Cloneable {
public void addSpellAbility(final SpellAbility s);
public void addStaticAbility(final StaticAbility st);
public void setHostCard(final Card host);
/**
* @return the triggers

View File

@@ -30,12 +30,11 @@ import forge.game.card.Card;
* </p>
*
* @author Forge
* @version $Id: KeywordsChange.java 27095 2014-08-17 07:32:24Z elcnesh $
*/
public class KeywordsChange {
private final KeywordCollection keywords = new KeywordCollection();
private final List<KeywordInterface> removeKeywordInterfaces = Lists.newArrayList();
private final List<String> removeKeywords = Lists.newArrayList();
public class KeywordsChange implements Cloneable {
private KeywordCollection keywords = new KeywordCollection();
private List<KeywordInterface> removeKeywordInterfaces = Lists.newArrayList();
private List<String> removeKeywords = Lists.newArrayList();
private boolean removeAllKeywords;
private boolean removeIntrinsicKeywords;
@@ -63,7 +62,7 @@ public class KeywordsChange {
this.removeAllKeywords = removeAll;
this.removeIntrinsicKeywords = removeIntrinsic;
}
public KeywordsChange(
final Collection<KeywordInterface> keywordList,
final Collection<KeywordInterface> removeKeywordInterfaces,
@@ -172,4 +171,49 @@ public class KeywordsChange {
removeIntrinsicKeywords = true;
}
}
public void setHostCard(final Card host) {
keywords.setHostCard(host);
for (KeywordInterface k : removeKeywordInterfaces) {
k.setHostCard(host);
}
}
public KeywordsChange copy(final Card host, final boolean lki) {
try {
KeywordsChange result = (KeywordsChange)super.clone();
result.keywords = new KeywordCollection();
for (KeywordInterface ki : this.keywords.getValues()) {
result.keywords.insert(ki.copy(host, lki));
}
result.removeKeywords = Lists.newArrayList(removeKeywords);
result.removeKeywordInterfaces = Lists.newArrayList();
for (KeywordInterface ki : this.removeKeywordInterfaces) {
removeKeywordInterfaces.add(ki.copy(host, lki));
}
return result;
} catch (final Exception ex) {
throw new RuntimeException("KeywordsChange : clone() error", ex);
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("<+");
sb.append(this.keywords);
sb.append("|-");
sb.append(this.removeKeywordInterfaces);
sb.append("|-");
sb.append(this.removeKeywords);
sb.append(">");
return sb.toString();
}
}

View File

@@ -76,7 +76,7 @@ public class ReplaceMoved extends ReplacementEffect {
return false;
}
if (zt.equals(ZoneType.Battlefield) && getHostCard().equals(affected)) {
if (zt.equals(ZoneType.Battlefield) && getHostCard().equals(affected) && !hasParam("BypassEtbCheck")) {
// would be an etb replacement effect that enters the battlefield
Card lki = CardUtil.getLKICopy(affected);
lki.setLastKnownZone(lki.getController().getZone(zt));

View File

@@ -42,48 +42,19 @@ public class TriggerChangesZoneAll extends Trigger {
}
private CardCollection filterCards(CardZoneTable table) {
CardCollection allCards = new CardCollection();
ZoneType destination = null;
List<ZoneType> origin = null;
if (hasParam("Destination")) {
if (!getParam("Destination").equals("Any")) {
destination = ZoneType.valueOf(getParam("Destination"));
if (!table.containsColumn(destination)) {
return allCards;
}
}
if (hasParam("Destination") && !getParam("Destination").equals("Any")) {
destination = ZoneType.valueOf(getParam("Destination"));
}
if (hasParam("Origin") && !getParam("Origin").equals("Any")) {
if (getParam("Origin") == null) {
return allCards;
}
final List<ZoneType> origin = ZoneType.listValueOf(getParam("Origin"));
for (ZoneType z : origin) {
if (table.containsRow(z)) {
if (destination != null) {
allCards.addAll(table.row(z).get(destination));
} else {
for (CardCollection c : table.row(z).values()) {
allCards.addAll(c);
}
}
}
}
} else if (destination != null) {
for (CardCollection c : table.column(destination).values()) {
allCards.addAll(c);
}
} else {
for (CardCollection c : table.values()) {
allCards.addAll(c);
}
origin = ZoneType.listValueOf(getParam("Origin"));
}
if (hasParam("ValidCards")) {
allCards = CardLists.getValidCards(allCards, getParam("ValidCards").split(","),
getHostCard().getController(), getHostCard(), null);
}
return allCards;
final String valid = this.getParamOrDefault("ValidCards", null);
return table.filterCards(origin, destination, valid, getHostCard(), null);
}
}

View File

@@ -25,7 +25,10 @@ import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.ability.effects.CharmEffect;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardUtil;
import forge.game.card.CardZoneTable;
import forge.game.keyword.KeywordInterface;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.Ability;
@@ -42,6 +45,7 @@ import io.sentry.event.BreadcrumbBuilder;
import java.util.*;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -376,11 +380,7 @@ public class TriggerHandler {
// Static triggers
for (final Trigger t : Lists.newArrayList(activeTriggers)) {
if (t.isStatic() && canRunTrigger(t, mode, runParams)) {
int x = 1 + handlePanharmonicon(t, runParams);
for (int i = 0; i < x; ++i) {
runSingleTrigger(t, runParams);
}
runSingleTrigger(t, runParams);
checkStatics = true;
}
@@ -448,7 +448,7 @@ public class TriggerHandler {
}
}
int x = 1 + handlePanharmonicon(t, runParams);;
int x = 1 + handlePanharmonicon(t, runParams, player);
for (int i = 0; i < x; ++i) {
runSingleTrigger(t, runParams);
@@ -692,46 +692,81 @@ public class TriggerHandler {
}
}
private int handlePanharmonicon(final Trigger t, final Map<String, Object> runParams) {
// Need to get the last info from the trigger host
final Card host = game.getChangeZoneLKIInfo(t.getHostCard());
final Player p = host.getController();
private int handlePanharmonicon(final Trigger t, final Map<String, Object> runParams, final Player p) {
Card host = t.getHostCard();
// not a changesZone trigger
if (t.getMode() != TriggerType.ChangesZone) {
// not a changesZone trigger or changesZoneAll
if (t.getMode() != TriggerType.ChangesZone && t.getMode() != TriggerType.ChangesZoneAll) {
return 0;
}
// leave battlefield trigger, might be dying
// only real changeszone look back for this
if (t.getMode() == TriggerType.ChangesZone && "Battlefield".equals(t.getParam("Origin"))) {
// Need to get the last info from the trigger host
host = game.getChangeZoneLKIInfo(host);
}
// not a Permanent you control
if (!host.isPermanent() || !host.isInZone(ZoneType.Battlefield)) {
return 0;
}
int n = 0;
for (final String kw : p.getKeywords()) {
if (kw.startsWith("Panharmonicon")) {
// Enter the Battlefield Trigger
if (runParams.get("Destination") instanceof String) {
final String dest = (String) runParams.get("Destination");
if ("Battlefield".equals(dest) && runParams.get("Card") instanceof Card) {
final Card card = (Card) runParams.get("Card");
final String valid = kw.split(":")[1];
if (card.isValid(valid.split(","), p, host, null)) {
n++;
if (t.getMode() == TriggerType.ChangesZone) {
// iterate over all cards
final List<Card> lastCards = CardLists.filterControlledBy(p.getGame().getLastStateBattlefield(), p);
for (final Card ck : lastCards) {
for (final KeywordInterface ki : ck.getKeywords()) {
final String kw = ki.getOriginal();
if (kw.startsWith("Panharmonicon")) {
// Enter the Battlefield Trigger
if (runParams.get("Destination") instanceof String) {
final String dest = (String) runParams.get("Destination");
if ("Battlefield".equals(dest) && runParams.get("Card") instanceof Card) {
final Card card = (Card) runParams.get("Card");
final String valid = kw.split(":")[1];
if (card.isValid(valid.split(","), p, ck, null)) {
n++;
}
}
}
} else if (kw.startsWith("Dieharmonicon")) {
// 700.4. The term dies means “is put into a graveyard from the battlefield.”
if (runParams.get("Origin") instanceof String) {
final String origin = (String) runParams.get("Origin");
if ("Battlefield".equals(origin) && runParams.get("Destination") instanceof String) {
final String dest = (String) runParams.get("Destination");
if ("Graveyard".equals(dest) && runParams.get("Card") instanceof Card) {
final Card card = (Card) runParams.get("Card");
final String valid = kw.split(":")[1];
if (card.isValid(valid.split(","), p, ck, null)) {
n++;
}
}
}
}
}
}
} else if (kw.startsWith("Dieharmonicon")) {
// 700.4. The term dies means “is put into a graveyard from the battlefield.”
if (runParams.get("Origin") instanceof String) {
final String origin = (String) runParams.get("Origin");
if ("Battlefield".equals(origin) && runParams.get("Destination") instanceof String) {
final String dest = (String) runParams.get("Destination");
if ("Graveyard".equals(dest) && runParams.get("Card") instanceof Card) {
final Card card = (Card) runParams.get("Card");
if (card.isCreature()) {
n++;
}
}
} else if (t.getMode() == TriggerType.ChangesZoneAll) {
final CardZoneTable table = (CardZoneTable) runParams.get("Cards");
// iterate over all cards
for (final Card ck : p.getCardsIn(ZoneType.Battlefield)) {
for (final KeywordInterface ki : ck.getKeywords()) {
final String kw = ki.getOriginal();
if (kw.startsWith("Panharmonicon")) {
// currently there is no ChangesZoneAll that would trigger on etb
final String valid = kw.split(":")[1];
if (!table.filterCards(null, ZoneType.Battlefield, valid, ck, null).isEmpty()) {
n++;
}
} else if (kw.startsWith("Dieharmonicon")) {
// 700.4. The term dies means “is put into a graveyard from the battlefield.”
final String valid = kw.split(":")[1];
if (!table.filterCards(ImmutableList.of(ZoneType.Battlefield), ZoneType.Graveyard,
valid, ck, null).isEmpty()) {
n++;
}
}
}

View File

@@ -13,7 +13,7 @@ public class TriggerWaiting {
private Map<String, Object> params;
private List<Trigger> triggers = null;
public TriggerWaiting(TriggerType m, Map<String, Object> p) {
public TriggerWaiting(TriggerType m, Map<String, Object> p) {
mode = m;
params = p;
}
@@ -25,7 +25,6 @@ public class TriggerWaiting {
public Map<String, Object> getParams() {
return params;
}
public List<Trigger> getTriggers() {
return triggers;
@@ -35,7 +34,7 @@ public class TriggerWaiting {
this.triggers = triggers;
}
@Override
@Override
public String toString() {
return TextUtil.concatWithSpace("Waiting trigger:", mode.toString(),"with", params.toString());
}

View File

@@ -1503,4 +1503,197 @@ public class GameSimulatorTest extends SimulationTestCase {
}
public void testRiotEnchantment() {
Game game = initAndCreateGame();
Player p = game.getPlayers().get(0);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
final String goblinName = "Zhur-Taa Goblin";
addCard("Rhythm of the Wild", p);
Card goblin = addCardToZone(goblinName, p, ZoneType.Hand);
addCard("Mountain", p);
addCard("Forest", p);
SpellAbility goblinSA = goblin.getFirstSpellAbility();
assertNotNull(goblinSA);
GameSimulator sim = createSimulator(game, p);
int score = sim.simulateSpellAbility(goblinSA).value;
assertTrue(score > 0);
Game simGame = sim.getSimulatedGameState();
Card simGoblin = findCardWithName(simGame, goblinName);
assertNotNull(simGoblin);
int effects = simGoblin.getCounters(CounterType.P1P1) + simGoblin.getKeywordMagnitude(Keyword.HASTE);
assertTrue(effects == 2);
}
public void testTeysaKarlovXathridNecromancer() {
// Teysa Karlov and Xathrid Necromancer dying at the same time makes 4 token
Game game = initAndCreateGame();
Player p = game.getPlayers().get(0);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
addCard("Teysa Karlov", p);
addCard("Xathrid Necromancer", p);
for (int i = 0; i < 4; i++) {
addCardToZone("Plains", p, ZoneType.Battlefield);
}
Card wrathOfGod = addCardToZone("Wrath of God", p, ZoneType.Hand);
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
SpellAbility wrathSA = wrathOfGod.getFirstSpellAbility();
assertNotNull(wrathSA);
GameSimulator sim = createSimulator(game, p);
int score = sim.simulateSpellAbility(wrathSA).value;
assertTrue(score > 0);
Game simGame = sim.getSimulatedGameState();
int numZombies = countCardsWithName(simGame, "Zombie");
assertTrue(numZombies == 4);
}
public void testDoubleTeysaKarlovXathridNecromancer() {
// Teysa Karlov dieing because of Legendary rule will make Xathrid Necromancer trigger 3 times
Game game = initAndCreateGame();
Player p = game.getPlayers().get(0);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
addCard("Teysa Karlov", p);
addCard("Xathrid Necromancer", p);
for (int i = 0; i < 3; i++) {
addCard("Plains", p);
}
addCard("Swamp", p);
Card second = addCardToZone("Teysa Karlov", p, ZoneType.Hand);
SpellAbility secondSA = second.getFirstSpellAbility();
GameSimulator sim = createSimulator(game, p);
int score = sim.simulateSpellAbility(secondSA).value;
assertTrue(score > 0);
Game simGame = sim.getSimulatedGameState();
int numZombies = countCardsWithName(simGame, "Zombie");
assertTrue(numZombies == 3);
}
public void testTeysaKarlovGitrogMonster() {
Game game = initAndCreateGame();
Player p = game.getPlayers().get(0);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
addCard("Teysa Karlov", p);
addCard("The Gitrog Monster", p);
addCard("Dryad Arbor", p);
for (int i = 0; i < 4; i++) {
addCard("Plains", p);
addCardToZone("Plains", p, ZoneType.Library);
}
Card armageddon = addCardToZone("Armageddon", p, ZoneType.Hand);
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
SpellAbility armageddonSA = armageddon.getFirstSpellAbility();
GameSimulator sim = createSimulator(game, p);
int score = sim.simulateSpellAbility(armageddonSA).value;
assertTrue(score > 0);
Game simGame = sim.getSimulatedGameState();
// Two cards drawn
assertTrue(simGame.getPlayers().get(0).getZone(ZoneType.Hand).size() == 2);
}
public void testTeysaKarlovGitrogMonsterGitrogDies() {
Game game = initAndCreateGame();
Player p = game.getPlayers().get(0);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
Card teysa = addCard("Teysa Karlov", p);
addCard("The Gitrog Monster", p);
addCard("Dryad Arbor", p);
String indestructibilityName = "Indestructibility";
Card indestructibility = addCard(indestructibilityName, p);
indestructibility.attachToEntity(teysa);
// update Indestructible state
game.getAction().checkStateEffects(true);
for (int i = 0; i < 4; i++) {
addCard("Plains", p);
addCardToZone("Plains", p, ZoneType.Library);
}
Card armageddon = addCardToZone("Wrath of God", p, ZoneType.Hand);
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
SpellAbility armageddonSA = armageddon.getFirstSpellAbility();
GameSimulator sim = createSimulator(game, p);
int score = sim.simulateSpellAbility(armageddonSA).value;
assertTrue(score > 0);
Game simGame = sim.getSimulatedGameState();
// One cards drawn
assertTrue(simGame.getPlayers().get(0).getZone(ZoneType.Hand).size() == 1);
}
public void testTeysaKarlovGitrogMonsterTeysaDies() {
Game game = initAndCreateGame();
Player p = game.getPlayers().get(0);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
addCard("Teysa Karlov", p);
Card gitrog = addCard("The Gitrog Monster", p);
addCard("Dryad Arbor", p);
String indestructibilityName = "Indestructibility";
Card indestructibility = addCard(indestructibilityName, p);
indestructibility.attachToEntity(gitrog);
// update Indestructible state
game.getAction().checkStateEffects(true);
for (int i = 0; i < 4; i++) {
addCard("Plains", p);
addCardToZone("Plains", p, ZoneType.Library);
}
Card armageddon = addCardToZone("Wrath of God", p, ZoneType.Hand);
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
SpellAbility armageddonSA = armageddon.getFirstSpellAbility();
GameSimulator sim = createSimulator(game, p);
int score = sim.simulateSpellAbility(armageddonSA).value;
assertTrue(score > 0);
Game simGame = sim.getSimulatedGameState();
// One cards drawn
assertTrue(simGame.getPlayers().get(0).getZone(ZoneType.Hand).size() == 1);
}
}

View File

@@ -2,8 +2,8 @@ Name:Dross Scorpion
ManaCost:4
Types:Artifact Creature Scorpion
PT:3/1
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Artifact.Creature+Other | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigUntap | TriggerDescription$ Whenever CARDNAME or another artifact creature dies,
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | OptionalDecider$ You | Execute$ TrigUntap | TriggerDescription$ you may untap target artifact.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Artifact.Creature+Other | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigUntap | TriggerDescription$ Whenever CARDNAME or another artifact creature dies, you may untap target artifact.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | OptionalDecider$ You | Execute$ TrigUntap | Secondary$ True | TriggerDescription$ Whenever CARDNAME or another artifact creature dies, you may untap target artifact.
SVar:TrigUntap:DB$Untap | ValidTgts$ Artifact | TgtPrompt$ Choose target artifact.
SVar:Picture:http://www.wizards.com/global/images/magic/general/dross_scorpion.jpg
Oracle:Whenever Dross Scorpion or another artifact creature dies, you may untap target artifact.

View File

@@ -2,7 +2,7 @@ Name:Naban, Dean of Iteration
ManaCost:1 U
Types:Legendary Creature Human Wizard
PT:2/1
S:Mode$ Continuous | Affected$ You | AddKeyword$ Panharmonicon:Wizard.YouCtrl | Description$ If a Wizard entering the battlefield under your control causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
K:Panharmonicon:Wizard.YouCtrl:If a Wizard entering the battlefield under your control causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
DeckHints:Type$Wizard
SVar:Picture:http://www.wizards.com/global/images/magic/general/naban_dean_of_iteration.jpg
Oracle:If a Wizard entering the battlefield under your control causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.

View File

@@ -1,6 +1,6 @@
Name:Panharmonicon
ManaCost:4
Types:Artifact
S:Mode$ Continuous | Affected$ You | AddKeyword$ Panharmonicon:Creature,Artifact | Description$ If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
K:Panharmonicon:Creature,Artifact:If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
SVar:Picture:http://www.wizards.com/global/images/magic/general/panharmonicon.jpg
Oracle:If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.

View File

@@ -2,6 +2,7 @@ Name:Awaken the Erstwhile
ManaCost:3 B B
Types:Sorcery
A:SP$ RepeatEach | Cost$ 3 B B | RepeatPlayers$ Player | RepeatSubAbility$ DBDiscard | SpellDescription$ Each player discards all the cards in their hand, then creates that many 2/2 black Zombie creature tokens.
SVar:DBToken:DB$ Token | TokenAmount$ X | TokenName$ Zombie | TokenTypes$ Creature,Zombie | TokenOwner$ You | TokenColors$ Black | TokenPower$ 2 | TokenToughness$ 2 | References$ X
SVar:DBToken:DB$ Token | TokenAmount$ X | TokenScript$ b_2_2_zombie | TokenOwner$ You | LegacyImage$ b 2 2 zombie rna | References$ X
SVar:X:Remembered$Amount
DeckHas:Ability$Token
Oracle:Each player discards all the cards in their hand, then creates that many 2/2 black Zombie creature tokens.

View File

@@ -1,3 +1,4 @@
# TODO: -- THIS SCRIPT NEEDS REWRITING (non-functional) --
Name:Captive Audience
ManaCost:5 B R
Types:Enchantment
@@ -21,4 +22,5 @@ T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | E
SVar:TrigChoose:DB$ ChooseCard | Defined$ You | Choices$ Player.Opponent | Mandatory$ True | SubAbility$ DBChangeZone
SVar:DBChangeZone:DB$ ChangeZone | Defined$ Self | Origin$ All | Destination$ Battlefield | GainControl$ True
SVar:RemRandomDeck:True
DeckHas:Ability$Token
Oracle:Captive Audience enters the battlefield under the control of an opponent of your choice.\nAt the beginning of your upkeep, choose one that hasn't been chosen —\n• Your life total becomes 4.\n• Discard your hand.\n• Each opponent creates five 2/2 black Zombie creature tokens.

View File

@@ -11,7 +11,8 @@ ALTERNATE
Name:Deploy
ManaCost:2 W U
Types:Instant
A:SP$ Token | Cost$ 2 W U | TokenAmount$ 2 | TokenOwner$ You | TokenScript$ c_1_1_a_thopter_flying | SubAbility$ DBGainLife | SpellDescription$ Create two 1/1 colorless Thopter artifact creature tokens with flying, then you gain 1 life for each creature you control.
A:SP$ Token | Cost$ 2 W U | TokenAmount$ 2 | TokenOwner$ You | TokenScript$ c_1_1_a_thopter_flying | LegacyImage$ c 1 1 a thopter flying rna | SubAbility$ DBGainLife | SpellDescription$ Create two 1/1 colorless Thopter artifact creature tokens with flying, then you gain 1 life for each creature you control.
SVar:DBGainLife:DB$ GainLife | LifeAmount$ X | References$ X
SVar:X:Count$TypeYouCtrl.Creature
DeckHas:Ability$Token
Oracle:Create two 1/1 colorless Thopter artifact creature tokens with flying, then you gain 1 life for each creature you control.

View File

@@ -5,7 +5,7 @@ Loyalty:3
A:AB$ Effect | Cost$ AddCounter<1/LOYALTY> | Name$ CARDNAME Effect | Triggers$ TrigSpellCast | SVars$ TrigPutCounter | SpellDescription$ Until end of turn, whenever a creature you control deals combat damage to a player, put a loyalty counter on CARDNAME.
SVar:X:Count$Valid Creature.YouCtrl
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ LOYALTY | CounterNum$ 1
A:AB$ Token | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenName$ Thopter | TokenTypes$ Artifact,Creature,Thopter | TokenOwner$ You | TokenColors$ Colorless | TokenPower$ 1 | TokenToughness$ 1 | TokenKeywords$ Flying | SubAbility$ DBGainLife | SpellDescription$ Create a 1/1 colorless Thopter artifact creature token with flying.
A:AB$ Token | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenScript$ c_1_1_a_thopter_flying | TokenOwner$ You | LegacyImage$ c 1 1 a thopter flying rna | SubAbility$ DBGainLife | SpellDescription$ Create a 1/1 colorless Thopter artifact creature token with flying.
SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1 | SpellDescription$ You gain 1 life.
DeckHas:Ability$Token
A:AB$ Dig | Cost$ SubCounter<7/LOYALTY> | Planeswalker$ True | DigNum$ 10 | ChangeNum$ 3 | DestinationZone$ Hand | DestinationZone2$ Library | LibraryPosition$ -1 | Choices$ Card.nonLand | SpellDescription$ Look at the top ten cards of your library. Put three of them into your hand and the rest on the bottom of your library in a random order.

View File

@@ -6,4 +6,5 @@ T:Mode$ SpellCast | ValidCard$ Instant | ValidActivatingPlayer$ You | TriggerZon
SVar:TrigGainLife:DB$GainLife | Defined$ You | LifeAmount$ 2 | SubAbility$ DBDraw
SVar:DBDraw:DB$Draw | Defined$ You | NumCards$ 1
SVar:X:Count$IfMainPhase
DeckHas:Ability$LifeGain
Oracle:When Dovin's Acuity enters the battlefield, you gain 2 life and draw a card.\nWhenever you cast an instant spell during your main phase, you may return Dovin's Acuity to its owner's hand.

View File

@@ -6,7 +6,7 @@ S:Mode$ Continuous | Affected$ Creature.OppCtrl | AddPower$ -1 | AddToughness$ -
SVar:PlayMain1:TRUE
SVar:RemRandomDeck:True
A:AB$ ChangeZone | Cost$ 4 W B | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card.OppOwn | SubAbility$ DBToken | SpellDescription$ Exile target card from an opponent's graveyard. If it was a creature card, you create a 1/1 white and black Spirit creature token with flying.
SVar:DBToken:DB$ Token | ConditionDefined$ Targeted | ConditionPresent$ Creature | ConditionCompare$ EQ1 | TokenAmount$ 1 | TokenScript$ wb_1_1_spirit_flying | TokenOwner$ You | SubAbility$ DBCleanup
SVar:DBToken:DB$ Token | ConditionDefined$ Targeted | ConditionPresent$ Creature | ConditionCompare$ EQ1 | TokenAmount$ 1 | TokenScript$ wb_1_1_spirit_flying | TokenOwner$ You | SubAbility$ DBCleanup | LegacyImage$ wb 1 1 spirit flying rna
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
DeckHas:Ability$Token
Oracle:Creatures you control get +1/+1.\nCreatures your opponents control get -1/-1.\n{2}{W}{B}: Exile target card from an opponent's graveyard. If it was a creature card, you create a 1/1 white and black Spirit creature token with flying.

View File

@@ -4,6 +4,6 @@ Types:Creature Human Shaman
PT:2/2
K:Haste
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigSacrifice | TriggerDescription$ At the beginning of your upkeep, you may sacrifice a creature. When you do, CARDNAME deals 2 damage to target opponent or planeswalker.
SVar:TrigSacrifice:DB$ Sacrifice | Optional$ True | SacValid$ Creature | Amount$ 1 | AILogic$ CARDNAME
SVar:TrigSacrifice:DB$ Sacrifice | Optional$ True | SacValid$ Creature | Amount$ 1
SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Opponent,Planeswalker | TgtPrompt$ Select target opponent or planeswalker | NumDmg$ 2
Oracle:Haste\nAt the beginning of your upkeep, you may sacrifice a creature. When you do, Fireblade Artist deals 2 damage to target opponent or planeswalker.

View File

@@ -1,7 +1,7 @@
Name:Goblin Gathering
ManaCost:2 R
Types:Sorcery
A:SP$ Token | Cost$ 2 R | TokenAmount$ X | TokenScript$ r_1_1_goblin | TokenOwner$ You | References$ X | SpellDescription$ Create a number of 1/1 red Goblin creature tokens equal to two plus the number of cards named CARDNAME in your graveyard.
A:SP$ Token | Cost$ 2 R | TokenAmount$ X | TokenScript$ r_1_1_goblin | LegacyImage$ r 1 1 goblin rna | TokenOwner$ You | References$ X | SpellDescription$ Create a number of 1/1 red Goblin creature tokens equal to two plus the number of cards named Goblin Gathering in your graveyard.
SVar:X:Count$ValidGraveyard Card.YouOwn+namedGoblin Gathering/Plus.2
DeckHints:Name$Goblin Gathering
DeckHas:Ability$Token

View File

@@ -10,4 +10,5 @@ A:AB$ ChangeZone | Cost$ -1/-1/Card.nonLand/nonland permanent with converted man
A:AB$ DealDamage | Cost$ PayLife<5/-5/Card> | Planeswalker$ True | Ultimate$ True | ValidTgts$ Player | NumDmg$ X | References$ X | SubAbility$ DBYouGainLife | SpellDescription$ CARDNAME deals damage to target player equal to the number of cards that player owns in exile and you gain that much life.
SVar:DBYouGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X | References$ X
SVar:X:TargetedPlayer$CardsInExile
DeckHas:Ability$LifeGain
Oracle:[+1]: Exile up to two target cards from a single graveyard. You gain 2 life if at least one creature card was exiled this way.\n[-1]: Exile target nonland permanent with converted mana cost 1 or less.\n[-5]: Kaya, Orzhov Usurper deals damage to target player equal to the number of cards that player owns in exile and you gain that much life.

View File

@@ -4,4 +4,5 @@ Types:Sorcery
A:SP$ DestroyAll | Cost$ W W B B | ValidCards$ Creature | RememberDestroyed$ True | SubAbility$ DBGainLife | SpellDescription$ Destroy all creatures. You gain life equal to the number of creatures you controlled that were destroyed this way.
SVar:DBGainLife:DB$ GainLife | LifeAmount$ X | References$ X
SVar:X:RememberedLKI$FilterControlledByRemembered_Number$1
DeckHas:Ability$LifeGain
Oracle:Destroy all creatures. You gain life equal to the number of creatures you controlled that were destroyed this way.

View File

@@ -2,7 +2,7 @@ Name:Knight of the Last Breath
ManaCost:5 W B
Types:Creature Giant Knight
PT:4/4
A:AB$ Token | Cost$ 3 Sac<1/Creature.nonToken+Other/another creature> | TokenAmount$ 1 | TokenScript$ wb_1_1_spirit_flying | TokenOwner$ You | SpellDescription$ Create a 1/1 white and black Spirit creature token with flying.
A:AB$ Token | Cost$ 3 Sac<1/Creature.nonToken+Other/another creature> | TokenAmount$ 1 | TokenScript$ wb_1_1_spirit_flying | TokenOwner$ You | LegacyImage$ wb 1 1 spirit flying rna | SpellDescription$ Create a 1/1 white and black Spirit creature token with flying.
SVar:RemRandomDeck:True
K:Afterlife:3
SVar:AIPreference:SacCost$Creature.cmcLE1

View File

@@ -3,10 +3,12 @@ ManaCost:3 U U
Types:Creature Octopus
PT:4/5
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create two 0/2 blue Illusion creature tokens with flying, it doesn't untap during its controller's next untap step.
SVar:TrigToken:DB$ Token | TokenAmount$ 2 | TokenName$ Illusion | TokenTypes$ Creature,Illusion | TokenOwner$ You | TokenColors$ Blue | TokenPower$ 0 | TokenToughness$ 2 | TokenKeywords$ HIDDEN This card doesn't untap during your next untap step. | Permanent$ True
SVar:TrigToken:DB$ Token | TokenAmount$ 2 | TokenScript$ u_0_2_illusion_mesmerize | TokenOwner$ You | LegacyImage$ u 0 2 illusion mesmerize rna
SVar:PlayMain1:TRUE
SVar:RemRandomDeck:True
S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Hexproof | CheckSVar$ X | SVarCompare$ GE1 | Description$ CARDNAME has hexproof as long as you control an Illusion.
SVar:X:Count$Valid Illusion.YouCtrl
SVar:BuffedBy:Illusion
DeckHas:Ability$Token
DeckHints:Type$Illusion
Oracle:When Mesmerizing Benthid enters the battlefield, create two 0/2 blue Illusion creature tokens with "Whenever this creature blocks a creature, that creature doesn't untap during its controller's next untap step."\nMesmerizing Benthid has hexproof as long as you control an Illusion.

View File

@@ -5,4 +5,5 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creatu
SVar:TrigFlipACoin:DB$FlipACoin | FlipUntilYouLose$ True | SaveNumFlipsToSVar$ MirrorAmount | NoCall$ True | LoseSubAbility$ DBCopies
SVar:DBCopies:DB$ CopyPermanent | PumpKeywords$ Haste | AtEOT$ Exile | Defined$ TriggeredCard | NumCopies$ MirrorAmount
SVar:MirrorAmount:Number$0
DeckHas:Ability$Token
Oracle:Whenever a nontoken creature enters the battlefield under your control, flip a coin until you lose a flip. For each flip you won, create a token that's a copy of that creature. Those tokens gain haste. Exile them at the beginning of the next end step.

View File

@@ -7,5 +7,6 @@ K:Trample
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigFlip | TriggerDescription$ When CARDNAME enters the battlefield, flip a coin for each creature that isn't a Demon, Dragons, or coins. Destroy each creature whose coin comes up tails.
SVar:TrigFlip:DB$ FlipACoin | FlipUntilYouLose$ True | SaveNumFlipsToSVar$ X | LoseSubAbility$ DBDestroy
SVar:DBDestroy:DB$ DestroyAll | ValidCards$ Creature.IsNotChosenType | References$ X
# TODO: -- THIS LINE NEEDS FIXING (doesn't reference the types correctly) --
SVar:X:Count$Valid Creature.isn
Oracle:Flying, trample\nWhen Rakdos, the Showstopper enters the battlefield, flip a coin for each creature that isn't a Demon, Devil, or Imp. Destroy each creature whose coin comes up tails.

View File

@@ -12,4 +12,5 @@ Name:Replicate
ManaCost:1 G U
Types:Sorcery
A:SP$ CopyPermanent | Cost$ 1 G U | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | SpellDescription$ Create a token that's a copy of target creature you control.
DeckHas:Ability$Token
Oracle:Create a token thats a copy of target creature you control.

View File

@@ -5,4 +5,5 @@ PT:6/6
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigEffect | TriggerDescription$ When CARDNAME enters the battlefield, your number of +1/+1 counters on creatures you control. Creatures your opponents control with power less than or equal to that number can't block this turn.
SVar:TrigEffect:DB$Effect | Name$ CARDNAME Effect | StaticAbilities$ KWPump
SVar:KWPump:Mode$ Continuous | EffectZone$ Command | Affected$ Creature.YouCtrl+powerLTY | AddHiddenKeyword$ CARDNAME can't block. | Description$ Creatures your opponents control with power less than or equal to that number can't block this turn.
# TODO: -- THIS SCRIPT NEEDS FIXING (doesn't have the count var) --
Oracle:When Rumbling Ruin enters the battlefield, count the number of +1/+1 counters on creatures you control. Creatures your opponents control with power less than or equal to that number can't block this turn.

View File

@@ -2,6 +2,6 @@ Name:Smothering Tithe
ManaCost:3 W
Types:Enchantment
T:Mode$ Drawn | ValidCard$ Card.OppOwn | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever an opponent draws a card, that player may pay {2}. If the player doesn't, you create a colorless Treasure artifact token with flying{T}, Sacrifice this artifact: Add one mana of any color.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenName$ Treasure | TokenTypes$ Artifact,Treasure | TokenOwner$ TriggeredPlayer | TokenColors$ Colorless | TokenImage$ c treasure | TokenAbilities$ ABTreasureMana | TokenAltImages$ c_treasure2,c_treasure3,c_treasure4
SVar:ABTreasureMana:DB$ Token | TokenAmount$ 1 | TokenName$
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_treasure_sac | TokenOwner$ TriggeredPlayer | LegacyImage$ c treasure
DeckHas:Ability$Token
Oracle:Whenever an opponent draws a card, that player may pay {2}. If the player doesn't, you create a colorless Treasure artifact token with "{T}, Sacrifice this artifact: Add one mana of any color."

View File

@@ -2,7 +2,7 @@ Name:Teysa Karlov
ManaCost:2 W B
Types:Legendary Creature Human Advisor
PT:2/4
S:Mode$ Continuous | Affected$ You | AddKeyword$ Dieharmonicon | Description$ If a creature dying causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
K:Dieharmonicon:Creature:If a creature dying causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
S:Mode$ Continuous | Affected$ Creature.token+YouCtrl | AddKeyword$ Vigilance & Lifelink | Description$ Creature tokens you control have vigilance and lifelink.
DeckHints:Ability$Token
Oracle:If a creature dying causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.\nCreature tokens you control have vigilance and lifelink.

View File

@@ -12,5 +12,6 @@ ALTERNATE
Name:Threat
ManaCost:2 R G
Types:Sorcery
A:SP$ Token | Cost$ 2 R G | TokenAmount$ 1 | TokenOwner$ You | TokenScript$ rg_4_4_beast_trample | SpellDescription$ Create a 4/4 red and green Beast creature token with trample.
A:SP$ Token | Cost$ 2 R G | TokenAmount$ 1 | TokenOwner$ You | TokenScript$ rg_4_4_beast_trample | LegacyImage$ rg 4 4 beast trample rna | SpellDescription$ Create a 4/4 red and green Beast creature token with trample.
DeckHas:Ability$Token
Oracle:Create a 4/4 red and green Beast creature token with trample.

View File

@@ -10,7 +10,7 @@ ALTERNATE
Name:Warden
ManaCost:3 W U
Types:Sorcery
A:SP$ Token | Cost$ 3 W U | TokenAmount$ 1 | TokenOwner$ You | TokenScript$ wu_4_4_sphinx_flying_vigilance | SpellDescription$ Create a 4/4 white and blue Sphinx creature token with flying and vigilance.
A:SP$ Token | Cost$ 3 W U | TokenAmount$ 1 | TokenOwner$ You | TokenScript$ wu_4_4_sphinx_flying_vigilance | LegacyImage$ wu 4 4 sphinx flying vigilance rna | SpellDescription$ Create a 4/4 white and blue Sphinx creature token with flying and vigilance.
Oracle:Create a 4/4 white and blue Sphinx creature token with flying and vigilance.

View File

@@ -3,7 +3,7 @@ ManaCost:5 W
Types:Creature Giant Soldier
PT:3/6
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a 1/1 white Human creature token.
SVar:TrigToken:DB$Token | TokenAmount$ 1 | TokenScript$ w_1_1_human | TokenOwner$ You | SpellDescription$ Create a 1/1 white Human creature token.
SVar:TrigToken:DB$Token | TokenAmount$ 1 | TokenScript$ w_1_1_human | LegacyImage$ w 1 1 human rna | TokenOwner$ You | SpellDescription$ Create a 1/1 white Human creature token.
DeckHints:Type$Human
DeckHas:Ability$Token
Oracle:When Watchful Giant enters the battlefield, create a 1/1 white Human creature token.

View File

@@ -38,6 +38,7 @@ b_2_2_zombie_m13.jpg https://downloads.cardforge.org/images/to
b_2_2_zombie_m14.jpg https://downloads.cardforge.org/images/tokens/b_2_2_zombie_m14.jpg
b_2_2_zombie_mbs.jpg https://downloads.cardforge.org/images/tokens/b_2_2_zombie_mbs.jpg
b_2_2_zombie_knight.jpg https://downloads.cardforge.org/images/tokens/b_2_2_zombie_knight.jpg
b_2_2_zombie_rna.jpg https://downloads.cardforge.org/images/tokens/b_2_2_zombie_rna.jpg
b_2_4_spider.jpg https://downloads.cardforge.org/images/tokens/b_2_4_spider.jpg
b_3_3_angel.jpg https://downloads.cardforge.org/images/tokens/b_3_3_angel.jpg
b_3_3_kavu.jpg https://downloads.cardforge.org/images/tokens/b_3_3_kavu.jpg
@@ -65,6 +66,7 @@ c_0_1_pest.jpg https://downloads.cardforge.org/images/to
c_0_1_prism.jpg https://downloads.cardforge.org/images/tokens/c_0_1_prism.jpg
c_0_2_wall.jpg https://downloads.cardforge.org/images/tokens/c_0_2_wall.jpg
c_10_10_eldrazi.jpg https://downloads.cardforge.org/images/tokens/c_10_10_eldrazi.jpg
c_1_1_a_thopter_flying_rna.jpg https://downloads.cardforge.org/images/tokens/c_1_1_a_thopter_flying_rna.jpg
c_1_1_eldrazi_scion.jpg https://downloads.cardforge.org/images/tokens/c_1_1_eldrazi_scion.jpg
c_1_1_eldrazi_scion2.jpg https://downloads.cardforge.org/images/tokens/c_1_1_eldrazi_scion2.jpg
c_1_1_eldrazi_scion3.jpg https://downloads.cardforge.org/images/tokens/c_1_1_eldrazi_scion3.jpg
@@ -103,6 +105,7 @@ c_5_5_tuktuk_the_returned.jpg https://downloads.cardforge.org/images/to
c_6_12_construct.jpg https://downloads.cardforge.org/images/tokens/c_6_12_construct.jpg
c_7_7_eldrazi.jpg https://downloads.cardforge.org/images/tokens/c_7_7_eldrazi.jpg
c_9_9_golem.jpg https://downloads.cardforge.org/images/tokens/c_9_9_golem.jpg
c_a_treasure_sac_rna.jpg https://downloads.cardforge.org/images/tokens/c_a_treasure_sac_rna.jpg
c_gold.jpg https://downloads.cardforge.org/images/tokens/c_gold.jpg
c_x_x_horror.jpg https://downloads.cardforge.org/images/tokens/c_x_x_horror.jpg
c_x_x_riptide_replicator.jpg https://downloads.cardforge.org/images/tokens/c_x_x_riptide_replicator.jpg
@@ -154,6 +157,7 @@ g_2_2_boar.jpg https://downloads.cardforge.org/images/to
g_2_2_cat_warrior.jpg https://downloads.cardforge.org/images/tokens/g_2_2_cat_warrior.jpg
g_2_2_lizard.jpg https://downloads.cardforge.org/images/tokens/g_2_2_lizard.jpg
g_2_2_ooze.jpg https://downloads.cardforge.org/images/tokens/g_2_2_ooze.jpg
g_2_2_ooze_rna.jpg https://downloads.cardforge.org/images/tokens/g_2_2_ooze_rna.jpg
g_2_2_wolf.jpg https://downloads.cardforge.org/images/tokens/g_2_2_wolf.jpg
g_2_2_wolf_bng.jpg https://downloads.cardforge.org/images/tokens/g_2_2_wolf_bng.jpg
g_2_2_wolf_isd.jpg https://downloads.cardforge.org/images/tokens/g_2_2_wolf_isd.jpg
@@ -176,11 +180,13 @@ g_3_3_boar.jpg https://downloads.cardforge.org/images/to
g_3_3_centaur.jpg https://downloads.cardforge.org/images/tokens/g_3_3_centaur.jpg
g_3_3_centaur_bng.jpg https://downloads.cardforge.org/images/tokens/g_3_3_centaur_bng.jpg
g_3_3_centaur_pro_black.jpg https://downloads.cardforge.org/images/tokens/g_3_3_centaur_pro_black.jpg
g_3_3_centaur_rna.jpg https://downloads.cardforge.org/images/tokens/g_3_3_centaur_rna.jpg
g_3_3_centaur_rtr.jpg https://downloads.cardforge.org/images/tokens/g_3_3_centaur_rtr.jpg
g_3_3_elephant.jpg https://downloads.cardforge.org/images/tokens/g_3_3_elephant.jpg
g_3_3_elephant_ody.jpg https://downloads.cardforge.org/images/tokens/g_3_3_elephant_ody.jpg
g_3_3_elephant_wwk.jpg https://downloads.cardforge.org/images/tokens/g_3_3_elephant_wwk.jpg
g_3_3_frog_lizard.jpg https://downloads.cardforge.org/images/tokens/g_3_3_frog_lizard.jpg
g_3_3_frog_lizard_rna.jpg https://downloads.cardforge.org/images/tokens/g_3_3_frog_lizard_rna.jpg
g_4_4_beast.jpg https://downloads.cardforge.org/images/tokens/g_4_4_beast.jpg
g_4_4_beast_zen.jpg https://downloads.cardforge.org/images/tokens/g_4_4_beast_zen.jpg
g_4_4_elemental.jpg https://downloads.cardforge.org/images/tokens/g_4_4_elemental.jpg
@@ -221,6 +227,7 @@ r_1_1_goblin_dom.jpg https://downloads.cardforge.org/images/to
r_1_1_goblin_m13.jpg https://downloads.cardforge.org/images/tokens/r_1_1_goblin_m13.jpg
r_1_1_goblin_nph.jpg https://downloads.cardforge.org/images/tokens/r_1_1_goblin_nph.jpg
r_1_1_goblin_rav.jpg https://downloads.cardforge.org/images/tokens/r_1_1_goblin_rav.jpg
r_1_1_goblin_rna.jpg https://downloads.cardforge.org/images/tokens/r_1_1_goblin_rna.jpg
r_1_1_goblin_rtr.jpg https://downloads.cardforge.org/images/tokens/r_1_1_goblin_rtr.jpg
r_1_1_goblin_scout.jpg https://downloads.cardforge.org/images/tokens/r_1_1_goblin_scout.jpg
r_1_1_goblin_som.jpg https://downloads.cardforge.org/images/tokens/r_1_1_goblin_som.jpg
@@ -261,6 +268,7 @@ rg_1_1_goblin_warrior.jpg https://downloads.cardforge.org/images/to
rg_2_2_satyr.jpg https://downloads.cardforge.org/images/tokens/rg_2_2_satyr.jpg
rg_3_4_stangg_twin.jpg https://downloads.cardforge.org/images/tokens/rg_3_4_stangg_twin.jpg
rg_4_4_giant_warrior.jpg https://downloads.cardforge.org/images/tokens/rg_4_4_giant_warrior.jpg
rg_4_4_beast_trample_rna.jpg https://downloads.cardforge.org/images/tokens/rg_4_4_beast_trample.jpg
rg_5_5_elemental.jpg https://downloads.cardforge.org/images/tokens/rg_5_5_elemental.jpg
rw_1_1_goblin_soldier.jpg https://downloads.cardforge.org/images/tokens/rw_1_1_goblin_soldier.jpg
rw_1_1_goblin_soldier_eve.jpg https://downloads.cardforge.org/images/tokens/rw_1_1_goblin_soldier_eve.jpg
@@ -268,6 +276,7 @@ rw_1_1_soldier.jpg https://downloads.cardforge.org/images/to
u_0_1_homunculus.jpg https://downloads.cardforge.org/images/tokens/u_0_1_homunculus.jpg
u_0_1_plant_wall.jpg https://downloads.cardforge.org/images/tokens/u_0_1_plant_wall.jpg
u_0_1_starfish.jpg https://downloads.cardforge.org/images/tokens/u_0_1_starfish.jpg
u_0_2_illusion_mesmerize_rna.jpg https://downloads.cardforge.org/images/tokens/u_0_2_illusion_mesmerize_rna.jpg
u_1_0_elemental.jpg https://downloads.cardforge.org/images/tokens/u_1_0_elemental.jpg
u_1_1_bird.jpg https://downloads.cardforge.org/images/tokens/u_1_1_bird.jpg
u_1_1_bird_eve.jpg https://downloads.cardforge.org/images/tokens/u_1_1_bird_eve.jpg
@@ -309,6 +318,7 @@ w_1_1_citizen.jpg https://downloads.cardforge.org/images/to
w_1_1_goldmeadow_harrier.jpg https://downloads.cardforge.org/images/tokens/w_1_1_goldmeadow_harrier.jpg
w_1_1_human.jpg https://downloads.cardforge.org/images/tokens/w_1_1_human.jpg
w_1_1_human_avr.jpg https://downloads.cardforge.org/images/tokens/w_1_1_human_avr.jpg
w_1_1_human_rna.jpg https://downloads.cardforge.org/images/tokens/w_1_1_human_rna.jpg
w_1_1_kithkin_soldier.jpg https://downloads.cardforge.org/images/tokens/w_1_1_kithkin_soldier.jpg
w_1_1_kithkin_soldier_shm.jpg https://downloads.cardforge.org/images/tokens/w_1_1_kithkin_soldier_shm.jpg
w_1_1_knight.jpg https://downloads.cardforge.org/images/tokens/w_1_1_knight.jpg
@@ -366,13 +376,15 @@ w_x_x_reflection.jpg https://downloads.cardforge.org/images/to
wb_1_1_cleric.jpg https://downloads.cardforge.org/images/tokens/wb_1_1_cleric.jpg
wb_1_1_spirit.jpg https://downloads.cardforge.org/images/tokens/wb_1_1_spirit.jpg
wb_1_1_spirit_gtc.jpg https://downloads.cardforge.org/images/tokens/wb_1_1_spirit_gtc.jpg
wb_1_1_spirit_rna.jpg https://downloads.cardforge.org/images/tokens/wb_1_1_spirit_rna.jpg
wrg_8_8_beast.jpg https://downloads.cardforge.org/images/tokens/wrg_8_8_beast.jpg
wu_1_1_bird.jpg https://downloads.cardforge.org/images/tokens/wu_1_1_bird.jpg
wu_4_4_sphinx_flying_vigilance_rna.jpg https://downloads.cardforge.org/images/tokens/wu_4_4_sphinx_flying_vigilance_rna.jpg
# Emblems
chandra_the_firebrand_effect.jpg https://downloads.cardforge.org/images/tokens/chandra_the_firebrand_effect.jpg
emblem_domri_chaos_bringer_rna.jpg https://downloads.cardforge.org/images/tokens/emblem_domri_chaos_bringer_rna.jpg
emblem_domri_rade.jpg https://downloads.cardforge.org/images/tokens/emblem_domri_rade.jpg
emblem_elspeth_knight_errant.jpg https://downloads.cardforge.org/images/tokens/emblem_elspeth_knight_errant.jpg
emblem_elspeth_suns_champion.jpg https://downloads.cardforge.org/images/tokens/emblem_elspeth_suns_champion.jpg

View File

@@ -0,0 +1,5 @@
Name:Treasure
ManaCost:no cost
Types:Artifact Treasure
A:AB$ Mana | Cost$ T Sac<1/CARDNAME|this artifact> | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color.
Oracle:{T}, Sacrifice this artifact: Add one mana of any color.

View File

@@ -0,0 +1,9 @@
Name:Illusion
Types:Creature Illusion
ManaCost:no cost
Colors:blue
PT:0/2
T:Mode$ AttackerBlocked | ValidBlocker$ Card.Self | Execute$ TrigPump | TriggerDescription$ Whenever this creature blocks a creature, that creature doesn't untap during its controller's next untap step.
SVar:TrigPump:DB$ Pump | Defined$ TriggeredAttacker | KW$ HIDDEN This card doesn't untap during your next untap step. | Permanent$ True
SVar:HasBlockEffect:TRUE
Oracle:Whenever this creature blocks a creature, that creature doesn't untap during its controller's next untap step.

View File

@@ -69,7 +69,7 @@ public class InputSelectFromTwoLists<T extends GameEntity> extends InputSelectMa
if ( valid1.contains(s) ) { selected1 = true; }
if ( valid2.contains(s) ) { selected2 = true; }
}
validChoices = selected1 ? ( selected2 ? FCollection.getEmpty() : valid2 ) : ( selected2 ? valid1 : validBoth );
validChoices = selected1 ? ( selected2 ? FCollection.<T>getEmpty() : valid2 ) : ( selected2 ? valid1 : validBoth );
setSelectables();
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public void run() {