Fixes graf cage

This commit is contained in:
Hans Mackowiak
2020-02-07 17:29:54 +00:00
committed by Michael Kamensky
parent c25971bba9
commit e062c83c02
25 changed files with 302 additions and 187 deletions

View File

@@ -125,11 +125,16 @@ public class GameAction {
Card copied = null; Card copied = null;
Card lastKnownInfo = null; Card lastKnownInfo = null;
// get the LKI from above like ChangeZoneEffect
if (params != null && params.containsKey(AbilityKey.CardLKI)) {
lastKnownInfo = (Card) params.get(AbilityKey.CardLKI);
}
if (c.isSplitCard()) { if (c.isSplitCard()) {
boolean resetToOriginal = false; boolean resetToOriginal = false;
if (c.isManifested()) { if (c.isManifested()) {
if (zoneFrom.is(ZoneType.Battlefield)) { if (fromBattlefield) {
// Make sure the card returns from the battlefield as the original card with two halves // Make sure the card returns from the battlefield as the original card with two halves
resetToOriginal = true; resetToOriginal = true;
} }
@@ -164,8 +169,22 @@ public class GameAction {
// Don't copy Tokens, copy only cards leaving the battlefield // Don't copy Tokens, copy only cards leaving the battlefield
// and returning to hand (to recreate their spell ability information) // and returning to hand (to recreate their spell ability information)
if (suppress || (!fromBattlefield && !toHand)) { if (suppress || (!fromBattlefield && !toHand)) {
lastKnownInfo = c;
copied = c; copied = c;
// if to Battlefield and it is caused by an replacement effect,
// try to get previous LKI if able
if (zoneTo.is(ZoneType.Battlefield)) {
if (cause != null && cause.isReplacementAbility()) {
ReplacementEffect re = cause.getReplacementEffect();
if (ReplacementType.Moved.equals(re.getMode())) {
lastKnownInfo = (Card) cause.getReplacingObject(AbilityKey.CardLKI);
}
}
}
if (lastKnownInfo == null) {
lastKnownInfo = CardUtil.getLKICopy(c);
}
} else { } else {
// if from Battlefield to Graveyard and Card does exist in LastStateBattlefield // if from Battlefield to Graveyard and Card does exist in LastStateBattlefield
// use that instead // use that instead
@@ -189,15 +208,17 @@ public class GameAction {
} }
if (!c.isToken()) { if (!c.isToken()) {
if (c.isCloned() || c.hasTextChangeState()) { boolean updateState = false;
c.removeCloneStates(); updateState |= c.removeCloneStates();
c.removeTextChangeStates(); updateState |= c.removeTextChangeStates();
if (updateState) {
c.updateStateForView(); c.updateStateForView();
} }
copied = CardFactory.copyCard(c, false); copied = CardFactory.copyCard(c, false);
if (fromBattlefield && copied.getCurrentStateName() != CardStateName.Original) { if (fromBattlefield) {
// when a card leaves the battlefield, ensure it's in its original state // when a card leaves the battlefield, ensure it's in its original state
// (we need to do this on the object before copying it, or it won't work correctly e.g. // (we need to do this on the object before copying it, or it won't work correctly e.g.
// on Transformed objects) // on Transformed objects)
@@ -227,59 +248,6 @@ public class GameAction {
} }
} }
// special rule for Worms of the Earth
if (toBattlefield && game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLandBattlefield)) {
// something that is already a Land cant enter the battlefield
Card noLandLKI = c;
if (!c.isLand()) {
// check if something would be a land
noLandLKI = CardUtil.getLKICopy(c);
// this check needs to check if this card would be on the battlefield
noLandLKI.setLastKnownZone(zoneTo);
CardCollection preList = new CardCollection(noLandLKI);
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
// fake etb counters thing, then if something changed,
// need to apply checkStaticAbilities again
if(!noLandLKI.isLand()) {
if (noLandLKI.putEtbCounters(null)) {
// counters are added need to check again
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
}
}
}
if(noLandLKI.isLand()) {
// if it isn't on the Stack, it stays in that Zone
if (!c.isInZone(ZoneType.Stack)) {
return c;
}
// if something would only be a land when entering the battlefield and not before
// put it into the graveyard instead
zoneTo = c.getOwner().getZone(ZoneType.Graveyard);
// reset facedown
copied.setState(CardStateName.Original, false);
copied.setManifested(false);
copied.updateStateForView();
// not to battlefield anymore!
toBattlefield = false;
if (c.isCloned() || c.hasTextChangeState()) {
c.removeCloneStates();
c.removeTextChangeStates();
c.updateStateForView();
}
if (copied.getCurrentStateName() != CardStateName.Original) {
copied.setState(CardStateName.Original, false);
}
copied.updateStateForView();
}
}
if (!suppress) { if (!suppress) {
if (zoneFrom == null) { if (zoneFrom == null) {
copied.getOwner().addInboundToken(copied); copied.getOwner().addInboundToken(copied);
@@ -298,15 +266,29 @@ public class GameAction {
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams); ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams);
if (repres != ReplacementResult.NotReplaced) { if (repres != ReplacementResult.NotReplaced) {
// reset failed manifested Cards back to original // reset failed manifested Cards back to original
if (c.isManifested()) { if (c.isManifested() && !c.isInZone(ZoneType.Battlefield)) {
c.turnFaceUp(false, false); c.turnFaceUp(false, false);
} }
if (game.getStack().isResolving(c) && !zoneTo.is(ZoneType.Graveyard) && repres == ReplacementResult.Prevented) {
copied.getOwner().removeInboundToken(copied);
return moveToGraveyard(c, cause, params);
}
copied.getOwner().removeInboundToken(copied); copied.getOwner().removeInboundToken(copied);
if (repres == ReplacementResult.Prevented) {
if (game.getStack().isResolving(c) && !zoneTo.is(ZoneType.Graveyard)) {
return moveToGraveyard(c, cause, params);
}
copied.clearDevoured();
copied.clearDelved();
copied.clearConvoked();
copied.clearExploited();
}
// was replaced with another Zone Change
if (toBattlefield && c.isInZone(ZoneType.Battlefield)) {
if (c.removeChangedState()) {
c.updateStateForView();
}
}
return c; return c;
} }
} }
@@ -370,7 +352,7 @@ public class GameAction {
// do ETB counters after zone add // do ETB counters after zone add
if (!suppress) { if (!suppress) {
if (toBattlefield ) { if (toBattlefield) {
copied.putEtbCounters(table); copied.putEtbCounters(table);
// enable replacement effects again // enable replacement effects again
for (final ReplacementEffect re : copied.getReplacementEffects()) { for (final ReplacementEffect re : copied.getReplacementEffects()) {
@@ -393,6 +375,14 @@ public class GameAction {
c.setMustAttackEntity(null); c.setMustAttackEntity(null);
} }
// for ETB trigger to work correct,
// the LKI needs to be the Card itself,
// or it might not updated correctly
// TODO be reworked when ZoneTrigger Update is done
if (toBattlefield) {
lastKnownInfo = c;
}
// Need to apply any static effects to produce correct triggers // Need to apply any static effects to produce correct triggers
checkStaticAbilities(); checkStaticAbilities();
game.getTriggerHandler().clearInstrinsicActiveTriggers(c, zoneFrom); game.getTriggerHandler().clearInstrinsicActiveTriggers(c, zoneFrom);
@@ -415,7 +405,7 @@ public class GameAction {
} }
game.getTriggerHandler().runTrigger(TriggerType.ChangesZone, runParams, true); game.getTriggerHandler().runTrigger(TriggerType.ChangesZone, runParams, true);
if (zoneFrom != null && zoneFrom.is(ZoneType.Battlefield) && !zoneFrom.getPlayer().equals(zoneTo.getPlayer())) { if (fromBattlefield && !zoneFrom.getPlayer().equals(zoneTo.getPlayer())) {
final Map<AbilityKey, Object> runParams2 = AbilityKey.mapFromCard(lastKnownInfo); final Map<AbilityKey, Object> runParams2 = AbilityKey.mapFromCard(lastKnownInfo);
runParams2.put(AbilityKey.OriginalController, zoneFrom.getPlayer()); runParams2.put(AbilityKey.OriginalController, zoneFrom.getPlayer());
if(params != null) { if(params != null) {
@@ -540,7 +530,7 @@ public class GameAction {
return moveTo(zoneTo, c, position, cause, null); return moveTo(zoneTo, c, position, cause, null);
} }
private Card moveTo(final Zone zoneTo, Card c, SpellAbility cause, Map<AbilityKey, Object> params) { public final Card moveTo(final Zone zoneTo, Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
// FThreads.assertExecutedByEdt(false); // This code must never be executed from EDT, // FThreads.assertExecutedByEdt(false); // This code must never be executed from EDT,
// use FThreads.invokeInNewThread to run code in a pooled thread // use FThreads.invokeInNewThread to run code in a pooled thread
return moveTo(zoneTo, c, null, cause, params); return moveTo(zoneTo, c, null, cause, params);
@@ -618,8 +608,12 @@ public class GameAction {
} }
public final Card moveToStack(final Card c, SpellAbility cause) { public final Card moveToStack(final Card c, SpellAbility cause) {
return moveToStack(c, cause, null);
}
public final Card moveToStack(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
final Zone stack = game.getStackZone(); final Zone stack = game.getStackZone();
return moveTo(stack, c, cause); return moveTo(stack, c, cause, params);
} }
public final Card moveToGraveyard(final Card c, SpellAbility cause) { public final Card moveToGraveyard(final Card c, SpellAbility cause) {
@@ -684,11 +678,15 @@ public class GameAction {
} }
public final Card moveToVariantDeck(Card c, ZoneType zone, int deckPosition, SpellAbility cause) { public final Card moveToVariantDeck(Card c, ZoneType zone, int deckPosition, SpellAbility cause) {
return moveToVariantDeck(c, zone, deckPosition, cause, null);
}
public final Card moveToVariantDeck(Card c, ZoneType zone, int deckPosition, SpellAbility cause, Map<AbilityKey, Object> params) {
final PlayerZone deck = c.getOwner().getZone(zone); final PlayerZone deck = c.getOwner().getZone(zone);
if (deckPosition == -1 || deckPosition > deck.size()) { if (deckPosition == -1 || deckPosition > deck.size()) {
deckPosition = deck.size(); deckPosition = deck.size();
} }
return changeZone(game.getZoneOf(c), deck, c, deckPosition, cause); return changeZone(game.getZoneOf(c), deck, c, deckPosition, cause, params);
} }
public final Card exile(final Card c, SpellAbility cause) { public final Card exile(final Card c, SpellAbility cause) {
@@ -723,16 +721,20 @@ public class GameAction {
} }
public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause) { public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause) {
return moveTo(name, c, libPosition, cause, null);
}
public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause, Map<AbilityKey, Object> params) {
// Call specific functions to set PlayerZone, then move onto moveTo // Call specific functions to set PlayerZone, then move onto moveTo
switch(name) { switch(name) {
case Hand: return moveToHand(c, cause); case Hand: return moveToHand(c, cause, params);
case Library: return moveToLibrary(c, libPosition, cause); case Library: return moveToLibrary(c, libPosition, cause, params);
case Battlefield: return moveToPlay(c, cause); case Battlefield: return moveToPlay(c, c.getController(), cause, params);
case Graveyard: return moveToGraveyard(c, cause); case Graveyard: return moveToGraveyard(c, cause, params);
case Exile: return exile(c, cause); case Exile: return exile(c, cause, params);
case Stack: return moveToStack(c, cause); case Stack: return moveToStack(c, cause, params);
case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause); case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause, params);
case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause); case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause, params);
default: // sideboard will also get there default: // sideboard will also get there
return moveTo(c.getOwner().getZone(name), c, cause); return moveTo(c.getOwner().getZone(name), c, cause);
} }

View File

@@ -34,8 +34,7 @@ public enum GlobalRuleChange {
onlyOneBlockerPerOpponent ("Each opponent can't block with more than one creature."), onlyOneBlockerPerOpponent ("Each opponent can't block with more than one creature."),
onlyTwoBlockers ("No more than two creatures can block each combat."), onlyTwoBlockers ("No more than two creatures can block each combat."),
toughnessAssignsDamage ("Each creature assigns combat damage equal to its toughness rather than its power."), toughnessAssignsDamage ("Each creature assigns combat damage equal to its toughness rather than its power."),
blankIsChaos("Each blank roll of the planar dice is a {CHAOS} roll."), blankIsChaos("Each blank roll of the planar dice is a {CHAOS} roll.");
noLandBattlefield("Lands can't enter the battlefield.");
private final String ruleText; private final String ruleText;

View File

@@ -54,7 +54,8 @@ public final class AbilityFactory {
"Execute", // DelayedTrigger "Execute", // DelayedTrigger
"FallbackAbility", // Complex Unless costs which can be unpayable "FallbackAbility", // Complex Unless costs which can be unpayable
"ChooseSubAbility", // Can choose a player via ChoosePlayer "ChooseSubAbility", // Can choose a player via ChoosePlayer
"CantChooseSubAbility" // Can't choose a player via ChoosePlayer "CantChooseSubAbility", // Can't choose a player via ChoosePlayer
"AnimateSubAbility" // For ChangeZone Effects to Animate before ETB
); );
public enum AbilityRecordType { public enum AbilityRecordType {

View File

@@ -1,6 +1,8 @@
package forge.game.ability.effects; package forge.game.ability.effects;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import forge.game.Game; import forge.game.Game;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
@@ -150,7 +152,19 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
} }
} }
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
if (destination == ZoneType.Battlefield) { if (destination == ZoneType.Battlefield) {
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
// need LKI before Animate does apply
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
source.addRemembered(c);
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
source.removeRemembered(c);
}
// Auras without Candidates stay in their current location // Auras without Candidates stay in their current location
if (c.isAura()) { if (c.isAura()) {
final SpellAbility saAura = c.getFirstAttachSpell(); final SpellAbility saAura = c.getFirstAttachSpell();
@@ -165,9 +179,9 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
Card movedCard = null; Card movedCard = null;
if (sa.hasParam("GainControl")) { if (sa.hasParam("GainControl")) {
c.setController(sa.getActivatingPlayer(), game.getNextTimestamp()); c.setController(sa.getActivatingPlayer(), game.getNextTimestamp());
movedCard = game.getAction().moveToPlay(c, sa.getActivatingPlayer(), sa); movedCard = game.getAction().moveToPlay(c, sa.getActivatingPlayer(), sa, moveParams);
} else { } else {
movedCard = game.getAction().moveTo(destination, c, libraryPos, sa); 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(); Card host = sa.getOriginalHost();
if (host == null) { if (host == null) {

View File

@@ -4,6 +4,8 @@ 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.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.GameCommand; import forge.GameCommand;
import forge.card.CardStateName; import forge.card.CardStateName;
import forge.game.Game; import forge.game.Game;
@@ -523,6 +525,17 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} }
} }
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
// need LKI before Animate does apply
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(tgtC));
hostCard.addRemembered(tgtC);
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
hostCard.removeRemembered(tgtC);
}
// Auras without Candidates stay in their current // Auras without Candidates stay in their current
// location // location
if (tgtC.isAura()) { if (tgtC.isAura()) {
@@ -530,13 +543,16 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (saAura != null) { if (saAura != null) {
saAura.setActivatingPlayer(sa.getActivatingPlayer()); saAura.setActivatingPlayer(sa.getActivatingPlayer());
if (!saAura.getTargetRestrictions().hasCandidates(saAura, false)) { if (!saAura.getTargetRestrictions().hasCandidates(saAura, false)) {
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
tgtC.removeChangedState();
}
continue; continue;
} }
} }
} }
movedCard = game.getAction().moveTo( movedCard = game.getAction().moveTo(
tgtC.getController().getZone(destination), tgtC, sa); tgtC.getController().getZone(destination), tgtC, sa, moveParams);
if (sa.hasParam("Unearth")) { if (sa.hasParam("Unearth")) {
movedCard.setUnearthed(true); movedCard.setUnearthed(true);
movedCard.addChangedCardKeywords(Lists.newArrayList("Haste"), null, false, false, movedCard.addChangedCardKeywords(Lists.newArrayList("Haste"), null, false, false,
@@ -983,6 +999,18 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (sa.hasParam("Tapped")) { if (sa.hasParam("Tapped")) {
c.setTapped(true); c.setTapped(true);
} }
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
// need LKI before Animate does apply
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
source.addRemembered(c);
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility"));
source.removeRemembered(c);
}
if (sa.hasParam("GainControl")) { if (sa.hasParam("GainControl")) {
Player newController = sa.getActivatingPlayer(); Player newController = sa.getActivatingPlayer();
if (sa.hasParam("NewController")) { if (sa.hasParam("NewController")) {
@@ -1109,7 +1137,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
c.addFaceupCommand(unanimate); c.addFaceupCommand(unanimate);
} }
} }
movedCard = game.getAction().moveTo(c.getController().getZone(destination), c, sa); movedCard = game.getAction().moveTo(c.getController().getZone(destination), c, sa, moveParams);
if (sa.hasParam("Tapped")) { if (sa.hasParam("Tapped")) {
movedCard.setTapped(true); movedCard.setTapped(true);
} }

View File

@@ -117,7 +117,7 @@ public class FlipCoinEffect extends SpellAbilityEffect {
} else { } else {
if (victory) { if (victory) {
if (sa.getParam("RememberWinner") != null) { if (sa.getParam("RememberWinner") != null) {
host.addRemembered(host); host.addRemembered(flipper);
} }
if (sa.hasAdditionalAbility("WinSubAbility")) { if (sa.hasAdditionalAbility("WinSubAbility")) {
@@ -126,7 +126,7 @@ public class FlipCoinEffect extends SpellAbilityEffect {
// runParams.put("Won","True"); // runParams.put("Won","True");
} else { } else {
if (sa.getParam("RememberLoser") != null) { if (sa.getParam("RememberLoser") != null) {
host.addRemembered(host); host.addRemembered(flipper);
} }
if (sa.hasAdditionalAbility("LoseSubAbility")) { if (sa.hasAdditionalAbility("LoseSubAbility")) {

View File

@@ -1,15 +1,11 @@
package forge.game.ability.effects; package forge.game.ability.effects;
import com.google.common.collect.Sets;
import forge.game.Game; import forge.game.Game;
import forge.game.GlobalRuleChange;
import forge.game.ability.AbilityUtils; 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.card.CardCollection; import forge.game.card.CardCollection;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.card.CardUtil;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -56,20 +52,8 @@ public class ManifestEffect extends SpellAbilityEffect {
} }
for(Card c : tgtCards) { for(Card c : tgtCards) {
//check if lki would be a land entering the battlefield
if (game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLandBattlefield)) {
Card lki = CardUtil.getLKICopy(c);
lki.turnFaceDownNoUpdate();
lki.setManifested(true);
lki.setLastKnownZone(p.getZone(ZoneType.Battlefield));
CardCollection preList = new CardCollection(lki);
game.getAction().checkStaticAbilities(false, Sets.newHashSet(lki), preList);
if (lki.isLand()) {
continue;
}
}
Card rem = c.manifest(p, sa); Card rem = c.manifest(p, sa);
if (sa.hasParam("RememberManifested") && rem != null) { if (sa.hasParam("RememberManifested") && rem != null && rem.isManifested()) {
source.addRemembered(rem); source.addRemembered(rem);
} }
} }

View File

@@ -633,7 +633,7 @@ public class Card extends GameEntity implements Comparable<Card> {
Card c = game.getAction().moveToPlay(this, p, sa); Card c = game.getAction().moveToPlay(this, p, sa);
// Add manifest demorph static ability for creatures // Add manifest demorph static ability for creatures
if (isCreature && !cost.isNoCost()) { if (c.isManifested() && isCreature && !cost.isNoCost()) {
// Add Manifest to original State // Add Manifest to original State
c.getState(CardStateName.Original).addSpellAbility(CardFactoryUtil.abilityManifestFaceUp(c, cost)); c.getState(CardStateName.Original).addSpellAbility(CardFactoryUtil.abilityManifestFaceUp(c, cost));
c.updateStateForView(); c.updateStateForView();
@@ -1168,10 +1168,10 @@ public class Card extends GameEntity implements Comparable<Card> {
return mustAttackEntity; return mustAttackEntity;
} }
public final void clearMustAttackEntity(final Player playerturn) { public final void clearMustAttackEntity(final Player playerturn) {
if (getController().equals(playerturn)) { if (getController().equals(playerturn)) {
mustAttackEntity = null; mustAttackEntity = null;
} }
mustAttackEntityThisTurn = null; mustAttackEntityThisTurn = null;
} }
public final GameEntity getMustAttackEntityThisTurn() { return mustAttackEntityThisTurn; } public final GameEntity getMustAttackEntityThisTurn() { return mustAttackEntityThisTurn; }
public final void setMustAttackEntityThisTurn(GameEntity entThisTurn) { mustAttackEntityThisTurn = entThisTurn; } public final void setMustAttackEntityThisTurn(GameEntity entThisTurn) { mustAttackEntityThisTurn = entThisTurn; }
@@ -2841,10 +2841,10 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
public final CardPlayOption mayPlay(final StaticAbility sta) { public final CardPlayOption mayPlay(final StaticAbility sta) {
if (sta == null) { if (sta == null) {
return null; return null;
} }
return mayPlay.get(sta); return mayPlay.get(sta);
} }
public final List<CardPlayOption> mayPlay(final Player player) { public final List<CardPlayOption> mayPlay(final Player player) {
@@ -3078,6 +3078,27 @@ public class Card extends GameEntity implements Comparable<Card> {
return Collections.unmodifiableMap(changedCardTypes); return Collections.unmodifiableMap(changedCardTypes);
} }
public boolean clearChangedCardTypes() {
if (changedCardTypes.isEmpty())
return false;
changedCardTypes.clear();
return true;
}
public boolean clearChangedCardKeywords() {
if (changedCardKeywords.isEmpty())
return false;
changedCardKeywords.clear();
return true;
}
public boolean clearChangedCardColors() {
if (changedCardColors.isEmpty())
return false;
changedCardColors.clear();
return true;
}
public Map<Long, KeywordsChange> getChangedCardKeywords() { public Map<Long, KeywordsChange> getChangedCardKeywords() {
return changedCardKeywords; return changedCardKeywords;
} }
@@ -3281,8 +3302,13 @@ public class Card extends GameEntity implements Comparable<Card> {
return clStates.getHost(); return clStates.getHost();
} }
public final void removeCloneStates() { public final boolean removeCloneStates() {
if (clonedStates.isEmpty()) {
return false;
}
clonedStates.clear(); clonedStates.clear();
updateCloneState(false);
return true;
} }
public final Map<Long, CardCloneStates> getCloneStates() { public final Map<Long, CardCloneStates> getCloneStates() {
@@ -3334,8 +3360,13 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
return false; return false;
} }
public final void removeTextChangeStates() { public final boolean removeTextChangeStates() {
if (textChangeStates.isEmpty()) {
return false;
}
textChangeStates.clear(); textChangeStates.clear();
updateCloneState(false);
return true;
} }
private final CardCloneStates getLastTextChangeState() { private final CardCloneStates getLastTextChangeState() {
@@ -3502,8 +3533,8 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
public final boolean toughnessAssignsDamage() { public final boolean toughnessAssignsDamage() {
return getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.toughnessAssignsDamage) return getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.toughnessAssignsDamage)
|| hasKeyword("CARDNAME assigns combat damage equal to its toughness rather than its power"); || hasKeyword("CARDNAME assigns combat damage equal to its toughness rather than its power");
} }
// How much combat damage does the card deal // How much combat damage does the card deal
@@ -3646,6 +3677,14 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
} }
public boolean clearChangedCardTraits() {
if (changedCardTraits.isEmpty()) {
return false;
}
changedCardTraits.clear();
return true;
}
// keywords are like flying, fear, first strike, etc... // keywords are like flying, fear, first strike, etc...
public final List<KeywordInterface> getKeywords() { public final List<KeywordInterface> getKeywords() {
return getKeywords(currentState); return getKeywords(currentState);
@@ -3687,7 +3726,7 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
public final void updateKeywords() { public final void updateKeywords() {
currentState.getView().updateKeywords(this, currentState); currentState.getView().updateKeywords(this, currentState);
} }
public final void addChangedCardKeywords(final List<String> keywords, final List<String> removeKeywords, public final void addChangedCardKeywords(final List<String> keywords, final List<String> removeKeywords,
@@ -4694,8 +4733,8 @@ public class Card extends GameEntity implements Comparable<Card> {
public final boolean hasDealtDamageToOpponentThisTurn() { public final boolean hasDealtDamageToOpponentThisTurn() {
for (final GameEntity e : getDamageHistory().getThisTurnDamaged()) { for (final GameEntity e : getDamageHistory().getThisTurnDamaged()) {
if (e instanceof Player) { if (e instanceof Player) {
final Player p = (Player) e; final Player p = (Player) e;
if (getController().isOpponentOf(p)) { if (getController().isOpponentOf(p)) {
return true; return true;
} }
@@ -5132,7 +5171,7 @@ public class Card extends GameEntity implements Comparable<Card> {
return eternalized; return eternalized;
} }
public final void setEternalized(final boolean b) { public final void setEternalized(final boolean b) {
eternalized = b; eternalized = b;
} }
public final int getExertedThisTurn() { public final int getExertedThisTurn() {
@@ -5835,10 +5874,10 @@ public class Card extends GameEntity implements Comparable<Card> {
continue; continue;
} }
if (drawbackOnly && params.containsKey("Execute")){ if (drawbackOnly && params.containsKey("Execute")){
String exec = this.getSVar(params.get("Execute")); String exec = this.getSVar(params.get("Execute"));
if (exec.contains("AB$")) { if (exec.contains("AB$")) {
continue; continue;
} }
} }
return true; return true;
} }
@@ -6482,4 +6521,22 @@ public class Card extends GameEntity implements Comparable<Card> {
public boolean canBlockAny() { public boolean canBlockAny() {
return !canBlockAny.isEmpty(); return !canBlockAny.isEmpty();
} }
public boolean removeChangedState() {
boolean updateState = false;
updateState |= removeCloneStates();
updateState |= removeTextChangeStates();
updateState |= clearChangedCardTypes();
updateState |= clearChangedCardKeywords();
updateState |= clearChangedCardColors();
updateState |= clearChangedCardTraits();
newPT.clear();
newPTCharacterDefining.clear();
clearEtbCounters();
return updateState;
}
} }

View File

@@ -84,6 +84,9 @@ public class ReplacementHandler {
affectedCard = (Card) runParams.get(AbilityKey.Affected); affectedCard = (Card) runParams.get(AbilityKey.Affected);
affectedLKI = CardUtil.getLKICopy(affectedCard); affectedLKI = CardUtil.getLKICopy(affectedCard);
affectedLKI.setLastKnownZone(affectedCard.getController().getZone(ZoneType.Battlefield)); affectedLKI.setLastKnownZone(affectedCard.getController().getZone(ZoneType.Battlefield));
// need to apply Counters to check its future state on the battlefield
affectedLKI.putEtbCounters(null);
preList.add(affectedLKI); preList.add(affectedLKI);
game.getAction().checkStaticAbilities(false, Sets.newHashSet(affectedLKI), preList); game.getAction().checkStaticAbilities(false, Sets.newHashSet(affectedLKI), preList);
checkAgain = true; checkAgain = true;

View File

@@ -2121,6 +2121,43 @@ public class GameSimulatorTest extends SimulationTestCase {
assertEquals(1, simWC.getPowerBonusFromCounters()); assertEquals(1, simWC.getPowerBonusFromCounters());
assertEquals(3, simGame.getPlayers().get(0).getCreaturesInPlay().size()); assertEquals(3, simGame.getPlayers().get(0).getCreaturesInPlay().size());
}
public void testEverAfterWithWaywardServant() {
Game game = initAndCreateGame();
Player p = game.getPlayers().get(0);
String everAfter = "Ever After";
String waywardServant = "Wayward Servant";
String goblin = "Raging Goblin";
for (int i = 0; i < 8; i++)
addCard("Swamp", p);
Card cardEverAfter = addCardToZone(everAfter, p, ZoneType.Hand);
Card cardWaywardServant = addCardToZone(waywardServant, p, ZoneType.Graveyard);
Card cardRagingGoblin = addCardToZone(goblin, p, ZoneType.Graveyard);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
game.getAction().checkStateEffects(true);
SpellAbility playSa = cardEverAfter.getSpellAbilities().get(0);
playSa.setActivatingPlayer(p);
playSa.getTargets().add(cardWaywardServant);
playSa.getTargets().add(cardRagingGoblin);
GameSimulator sim = createSimulator(game, p);
int origScore = sim.getScoreForOrigGame().value;
int score = sim.simulateSpellAbility(playSa).value;
assertTrue(String.format("score=%d vs. origScore=%d", score, origScore), score > origScore);
Game simGame = sim.getSimulatedGameState();
Card simGoblin = findCardWithName(simGame, goblin);
simGame.getAction().checkStateEffects(true);
simGame.getPhaseHandler().devAdvanceToPhase(PhaseType.MAIN2);
assertEquals(21, simGame.getPlayers().get(0).getLife());
assertEquals(true, simGoblin.getType().hasSubtype("Zombie"));
} }
} }

View File

@@ -3,9 +3,9 @@ ManaCost:G W
Types:Creature Cat Types:Creature Cat
PT:3/3 PT:3/3
A:AB$ Pump | Cost$ G W | KW$ Indestructible | Defined$ Self | SpellDescription$ CARDNAME gains indestructible until end of turn. A:AB$ Pump | Cost$ G W | KW$ Indestructible | Defined$ Self | SpellDescription$ CARDNAME gains indestructible until end of turn.
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | TriggerController$ TriggeredCardController | Execute$ DBAnimate | TriggerDescription$ When CARDNAME dies, return it to the battlefield. It's an Aura enchantment with enchant creature you control and CARDNAME has "{G}{W}: Enchanted creature gains indestructible until end of turn," and it loses all other abilities. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | TriggerController$ TriggeredCardController | Execute$ DBReturn | TriggerDescription$ When CARDNAME dies, return it to the battlefield. It's an Aura enchantment with enchant creature you control and CARDNAME has "{G}{W}: Enchanted creature gains indestructible until end of turn," and it loses all other abilities.
SVar:DBAnimate:DB$ Animate | Defined$ TriggeredCard | Types$ Enchantment,Aura | RemoveCardTypes$ True | RemoveAllAbilities$ True | Keywords$ Enchant creature you control | Abilities$ SPAttach,ABPump | Permanent$ True | SubAbility$ DBReturn SVar:DBReturn:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Battlefield | AnimateSubAbility$ DBAnimate
SVar:DBReturn:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Battlefield SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Enchantment,Aura | RemoveCardTypes$ True | RemoveAllAbilities$ True | Keywords$ Enchant creature you control | Abilities$ SPAttach,ABPump | Permanent$ True
SVar:SPAttach:SP$ Attach | Cost$ 0 | ValidTgts$ Creature.YouCtrl | AILogic$ Pump SVar:SPAttach:SP$ Attach | Cost$ 0 | ValidTgts$ Creature.YouCtrl | AILogic$ Pump
SVar:ABPump:AB$ Pump | Cost$ G W | KW$ Indestructible | Defined$ Enchanted | SpellDescription$ Enchanted creature gains indestructible until end of turn. SVar:ABPump:AB$ Pump | Cost$ G W | KW$ Indestructible | Defined$ Enchanted | SpellDescription$ Enchanted creature gains indestructible until end of turn.
Oracle:{G}{W}: Bronzehide Lion gains indestructible until end of turn.\nWhen Bronzehide Lion dies, return it to the battlefield. It's an Aura enchantment with enchant creature you control and "{G}{W}: Enchanted creature gains indestructible until end of turn," and it loses all other abilities. Oracle:{G}{W}: Bronzehide Lion gains indestructible until end of turn.\nWhen Bronzehide Lion dies, return it to the battlefield. It's an Aura enchantment with enchant creature you control and "{G}{W}: Enchanted creature gains indestructible until end of turn," and it loses all other abilities.

View File

@@ -3,8 +3,8 @@ ManaCost:3 B B
Types:Legendary Creature Human Minion 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 | SubAbility$ 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$ Targeted | 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

@@ -1,8 +1,8 @@
Name:Dance of the Manse Name:Dance of the Manse
ManaCost:X W U ManaCost:X W U
Types:Sorcery Types:Sorcery
A:SP$ ChangeZone | Cost$ X W U | Announce$ X | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Artifact.cmcLEX+YouOwn,Enchantment.cmcLEX+YouOwn+nonAura | TgtPrompt$ Select target artifact or non-Aura enchantment in your graveyard | TargetMin$ 0 | TargetMax$ X | SubAbility$ DBAnimate | SpellDescription$ Return up to X target artifact and/or non-Aura enchantment cards with converted mana cost X or less from your graveyard to the battlefield. If X is 6 or more, those permanents are 4/4 creatures in addition to their other types. A:SP$ ChangeZone | Cost$ X W U | Announce$ X | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Artifact.cmcLEX+YouOwn,Enchantment.cmcLEX+YouOwn+nonAura | TgtPrompt$ Select target artifact or non-Aura enchantment in your graveyard | TargetMin$ 0 | TargetMax$ X | AnimateSubAbility$ DBAnimate | SpellDescription$ Return up to X target artifact and/or non-Aura enchantment cards with converted mana cost X or less from your graveyard to the battlefield. If X is 6 or more, those permanents are 4/4 creatures in addition to their other types.
SVar:DBAnimate:DB$ Animate | Defined$ Targeted | Types$ Creature | Power$ 4 | Toughness$ 4 | Permanent$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ GE6 | References$ X SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Creature | Power$ 4 | Toughness$ 4 | Permanent$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ GE6 | References$ X
SVar:X:Count$xPaid SVar:X:Count$xPaid
AI:RemoveDeck:All AI:RemoveDeck:All
Oracle:Return up to X target artifact and/or non-Aura enchantment cards each with converted mana cost X or less from your graveyard to the battlefield. If X is 6 or more, those permanents are 4/4 creatures in addition to their other types. Oracle:Return up to X target artifact and/or non-Aura enchantment cards each with converted mana cost X or less from your graveyard to the battlefield. If X is 6 or more, those permanents are 4/4 creatures in addition to their other types.

View File

@@ -3,8 +3,6 @@ ManaCost:3 B B
Types:Creature Zombie Horror Types:Creature Zombie Horror
PT:3/5 PT:3/5
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.DamagedBy | Execute$ TrigChange | TriggerDescription$ Whenever a creature dealt damage by CARDNAME this turn dies, return it to the battlefield under your control. That creature is a black Zombie in addition to its other colors and types. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.DamagedBy | Execute$ TrigChange | TriggerDescription$ Whenever a creature dealt damage by CARDNAME this turn dies, return it to the battlefield under your control. That creature is a black Zombie in addition to its other colors and types.
SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | Defined$ TriggeredCard | RememberChanged$ True | SubAbility$ DBAnimate SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | Defined$ TriggeredCard | AnimateSubAbility$ DBAnimate
SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True | SubAbility$ DBCleanup SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/dread_slaver.jpg
Oracle:Whenever a creature dealt damage by Dread Slaver this turn dies, return it to the battlefield under your control. That creature is a black Zombie in addition to its other colors and types. Oracle:Whenever a creature dealt damage by Dread Slaver this turn dies, return it to the battlefield under your control. That creature is a black Zombie in addition to its other colors and types.

View File

@@ -1,9 +1,9 @@
Name:Ever After Name:Ever After
ManaCost:4 B B ManaCost:4 B B
Types:Sorcery Types:Sorcery
A:SP$ ChangeZone | Cost$ 4 B B | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouOwn | TargetMin$ 0 | TargetMax$ 2 | SubAbility$ Animate | SpellDescription$ Return up to two target creature cards from your graveyard onto the battlefield. A:SP$ ChangeZone | Cost$ 4 B B | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouOwn | TargetMin$ 0 | TargetMax$ 2 | AnimateSubAbility$ Animate | SubAbility$ DBPut | SpellDescription$ Return up to two target creature cards from your graveyard onto the battlefield.
SVar:Animate:DB$Animate | Defined$ Targeted | Types$ Zombie | Colors$ Black | Permanent$ True | SubAbility$ DBPut | SpellDescription$ Each of those creatures is a black Zombie in addition to its other colors and types. SVar:Animate:DB$Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True | SpellDescription$ Each of those creatures is a black Zombie in addition to its other colors and types.
SVar:DBPut:DB$ChangeZone | Origin$ Stack | Destination$ Library | LibraryPosition$ -1 | SpellDescription$ Put Ever After on the bottom of its owner's library. SVar:DBPut:DB$ChangeZone | Origin$ Stack | Destination$ Library | LibraryPosition$ -1 | SpellDescription$ Put Ever After on the bottom of its owner's library.
DeckHints:Ability$Graveyard & Ability$Discard DeckHints:Ability$Graveyard & Ability$Discard & Type$Zombie
SVar:Picture:http://www.wizards.com/global/images/magic/general/ever_after.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/ever_after.jpg
Oracle:Return up to two target creature cards from your graveyard to the battlefield. Each of those creatures is a black Zombie in addition to its other colors and types. Put Ever After on the bottom of its owner's library. Oracle:Return up to two target creature cards from your graveyard to the battlefield. Each of those creatures is a black Zombie in addition to its other colors and types. Put Ever After on the bottom of its owner's library.

View File

@@ -1,10 +1,8 @@
Name:Grafdigger's Cage Name:Grafdigger's Cage
ManaCost:1 ManaCost:1
Types:Artifact Types:Artifact
R:Event$Moved | ActiveZones$ Battlefield | Origin$ Graveyard | Destination$ Battlefield | ValidCard$ Creature | Prevent$ True | Description$ Creature cards in graveyards and libraries can't enter the battlefield. R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Description$ Creature cards in graveyards and libraries can't enter the battlefield.
R:Event$Moved | ActiveZones$ Battlefield | Origin$ Library | Destination$ Battlefield | ValidCard$ Creature | Prevent$ True S:Mode$ CantBeCast | Origin$ Graveyard,Library | Description$ Players can't cast spells from graveyards or libraries.
S:Mode$ CantBeCast | Origin$ Graveyard | Description$ Players can't cast spells from graveyards or libraries.
S:Mode$ CantBeCast | Origin$ Library
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/grafdiggers_cage.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/grafdiggers_cage.jpg

View File

@@ -1,12 +1,9 @@
Name:Grave Betrayal Name:Grave Betrayal
ManaCost:5 B B ManaCost:5 B B
Types:Enchantment Types:Enchantment
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouDontCtrl | TriggerZones$ Battlefield | Execute$ TrigEffect | TriggerDescription$ Whenever a creature you don't control dies, return it to the battlefield under your control with an additional +1/+1 counter on it at the beginning of the next end step. That creature is a black Zombie in addition to its other colors and types. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouDontCtrl | TriggerZones$ Battlefield | Execute$ DelTrig | TriggerDescription$ Whenever a creature you don't control dies, return it to the battlefield under your control with an additional +1/+1 counter on it at the beginning of the next end step. That creature is a black Zombie in addition to its other colors and types.
SVar:TrigEffect:DB$ Effect | Name$ Grave Betrayal Effect | Triggers$ TrigEOT | SVars$ GBReturn,GBCounter,GBZombify,GBCleanup | References$ GBReturn,GBCounter,GBZombify,GBCleanup | RememberObjects$ TriggeredCard | Duration$ Permanent SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ GBReturn | RememberObjects$ Remembered | SubAbility$ DBCleanup | TriggerDescription$ Return creature to the battlefield under your control with an additional +1/+1 counter on it at the beginning of the next end step. It is a black Zombie in addition to its other colors and types.
SVar:TrigEOT:Mode$ Phase | Phase$ End of Turn | Execute$ GBReturn | TriggerDescription$ Return creature to the battlefield under your control with an additional +1/+1 counter on it at the beginning of the next end step. It is a black Zombie in addition to its other colors and types. SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:GBReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | RememberChanged$ True | ForgetOtherRemembered$ True | SubAbility$ GBCounter SVar:GBReturn:DB$ ChangeZone | Defined$ DelayTriggerRemembered | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | WithCounters$ P1P1_1 | AnimateSubAbility$ GBZombify
SVar:GBCounter:DB$ PutCounter | Defined$ Remembered | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ GBZombify SVar:GBZombify:DB$ Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True
SVar:GBZombify:DB$ Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True | SubAbility$ GBCleanup
SVar:GBCleanup:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
SVar:Picture:http://www.wizards.com/global/images/magic/general/grave_betrayal.jpg
Oracle:Whenever a creature you don't control dies, return it to the battlefield under your control with an additional +1/+1 counter on it at the beginning of the next end step. That creature is a black Zombie in addition to its other colors and types. Oracle:Whenever a creature you don't control dies, return it to the battlefield under your control with an additional +1/+1 counter on it at the beginning of the next end step. That creature is a black Zombie in addition to its other colors and types.

View File

@@ -2,9 +2,8 @@ Name:Grimoire of the Dead
ManaCost:4 ManaCost:4
Types:Legendary Artifact Types:Legendary Artifact
A:AB$ PutCounter | Cost$ 1 T Discard<1/Card> | Defined$ Self | CounterType$ STUDY | CounterNum$ 1 | SpellDescription$ Put a study counter on Grimoire of the Dead. A:AB$ PutCounter | Cost$ 1 T Discard<1/Card> | Defined$ Self | CounterType$ STUDY | CounterNum$ 1 | SpellDescription$ Put a study counter on Grimoire of the Dead.
A:AB$ ChangeZoneAll | Cost$ T SubCounter<3/STUDY> Sac<1/CARDNAME> | ChangeType$ Creature | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | RememberChanged$ True | SubAbility$ DBAnimate | SpellDescription$ Put all creature cards in all graveyards onto the battlefield under your control. They are black Zombies in addition to their other colors and types. A:AB$ ChangeZoneAll | Cost$ T SubCounter<3/STUDY> Sac<1/CARDNAME> | ChangeType$ Creature | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | AnimateSubAbility$ DBAnimate | SpellDescription$ Put all creature cards in all graveyards onto the battlefield under your control. They are black Zombies in addition to their other colors and types.
SVar:DBAnimate:DB$ AnimateAll | ValidCards$ Creature.IsRemembered | Colors$ Black | Types$ Zombie | Permanent$ True | SubAbility$ DBCleanup SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Colors$ Black | Types$ Zombie | Permanent$ True
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:IsReanimatorCard:TRUE SVar:IsReanimatorCard:TRUE
SVar:Picture:http://www.wizards.com/global/images/magic/general/grimoire_of_the_dead.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/grimoire_of_the_dead.jpg

View File

@@ -5,7 +5,7 @@ PT:3/3
K:Vigilance K:Vigilance
K:Menace K:Menace
K:Lifelink K:Lifelink
R:Event$Moved | ActiveZones$ Battlefield | Origin$ Graveyard | Destination$ Battlefield | ValidCard$ Creature | Prevent$ True | Description$ Creature cards in graveyards can't enter the battlefield. R:Event$Moved | ActiveZones$ Battlefield | Origin$ Graveyard | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Description$ Creature cards in graveyards can't enter the battlefield.
S:Mode$ CantBeCast | Origin$ Graveyard | Description$ Players can't cast spells from graveyards. S:Mode$ CantBeCast | Origin$ Graveyard | Description$ Players can't cast spells from graveyards.
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
Oracle:Vigilance, menace, lifelink\nCreature cards in graveyards can't enter the battlefield.\nPlayers can't cast spells from graveyards. Oracle:Vigilance, menace, lifelink\nCreature cards in graveyards can't enter the battlefield.\nPlayers can't cast spells from graveyards.

View File

@@ -4,8 +4,7 @@ Types:Legendary Planeswalker Liliana
Loyalty:5 Loyalty:5
A:AB$ Token | Cost$ AddCounter<1/LOYALTY> | TokenAmount$ 1 | TokenScript$ b_2_2_zombie | TokenOwner$ You | LegacyImage$ b 2 2 zombie akh | Planeswalker$ True | SubAbility$ DBMill | SpellDescription$ Create a 2/2 black Zombie creature token. Put the top two cards of your library into your graveyard. A:AB$ Token | Cost$ AddCounter<1/LOYALTY> | TokenAmount$ 1 | TokenScript$ b_2_2_zombie | TokenOwner$ You | LegacyImage$ b 2 2 zombie akh | Planeswalker$ True | SubAbility$ DBMill | SpellDescription$ Create a 2/2 black Zombie creature token. Put the top two cards of your library into your graveyard.
SVar:DBMill:DB$Mill | Defined$ You | NumCards$ 2 SVar:DBMill:DB$Mill | Defined$ You | NumCards$ 2
A:AB$ ChangeZone | Cost$ SubCounter<3/LOYALTY> | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouCtrl | SubAbility$ Animate | TgtPrompt$ Select target creature card from your graveyard | Planeswalker$ True | SpellDescription$ Return target creature card from your graveyard to the battlefield. That creature is a black Zombie in addition to its other colors and types. A:AB$ ChangeZone | Cost$ SubCounter<3/LOYALTY> | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouCtrl | AnimateSubAbility$ Animate | TgtPrompt$ Select target creature card from your graveyard | Planeswalker$ True | SpellDescription$ Return target creature card from your graveyard to the battlefield. That creature is a black Zombie in addition to its other colors and types.
SVar:Animate:DB$Animate | Defined$ Targeted | Types$ Zombie | Colors$ Black | Permanent$ True SVar:Animate:DB$Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True
A:AB$ DestroyAll | Cost$ SubCounter<7/LOYALTY> | Ultimate$ True | ValidCards$ Creature.nonZombie | Planeswalker$ True | SpellDescription$ Destroy all non-Zombie creatures. A:AB$ DestroyAll | Cost$ SubCounter<7/LOYALTY> | Ultimate$ True | ValidCards$ Creature.nonZombie | Planeswalker$ True | SpellDescription$ Destroy all non-Zombie creatures.
SVar:Picture:http://www.wizards.com/global/images/magic/general/liliana_deaths_majesty.jpg
Oracle:[+1]: Create a 2/2 black Zombie creature token. Put the top two cards of your library into your graveyard.\n[-3]: Return target creature card from your graveyard to the battlefield. That creature is a black Zombie in addition to its other colors and types.\n[-7]: Destroy all non-Zombie creatures. Oracle:[+1]: Create a 2/2 black Zombie creature token. Put the top two cards of your library into your graveyard.\n[-3]: Return target creature card from your graveyard to the battlefield. That creature is a black Zombie in addition to its other colors and types.\n[-7]: Destroy all non-Zombie creatures.

View File

@@ -3,9 +3,8 @@ ManaCost:5 B B
Types:Legendary Creature Human Wizard Types:Legendary Creature Human Wizard
PT:4/4 PT:4/4
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.OppCtrl | TriggerZones$ Battlefield | Execute$ TrigReturn | OptionalDecider$ You | TriggerDescription$ Whenever a creature an opponent controls dies, you may pay {1}{B}. If you do, return that card to the battlefield under your control. If it's a creature, it's a Zombie in addition to its other creature types. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.OppCtrl | TriggerZones$ Battlefield | Execute$ TrigReturn | OptionalDecider$ You | TriggerDescription$ Whenever a creature an opponent controls dies, you may pay {1}{B}. If you do, return that card to the battlefield under your control. If it's a creature, it's a Zombie in addition to its other creature types.
SVar:TrigReturn:AB$ ChangeZone | Cost$ 1 B | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | Defined$ TriggeredCard | RememberChanged$ True | SubAbility$ Animate SVar:TrigReturn:AB$ ChangeZone | Cost$ 1 B | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | Defined$ TriggeredCard | AnimateSubAbility$ Animate
SVar:Animate:DB$ Animate | Defined$ Remembered | Types$ Zombie | Permanent$ True | ConditionDefined$ Remembered | ConditionPresent$ Creature | SubAbility$ DBCleanup SVar:Animate:DB$ Animate | Defined$ Remembered | Types$ Zombie | Permanent$ True | ConditionDefined$ Remembered | ConditionPresent$ Creature
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
A:AB$ Regenerate | ValidTgts$ Zombie | TgtPrompt$ Select target Zombie | Cost$ 1 B | SpellDescription$ Regenerate target Zombie. A:AB$ Regenerate | ValidTgts$ Zombie | TgtPrompt$ Select target Zombie | Cost$ 1 B | SpellDescription$ Regenerate target Zombie.
SVar:Picture:http://www.wizards.com/global/images/magic/general/lim_dul_the_necromancer.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/lim_dul_the_necromancer.jpg
Oracle:Whenever a creature an opponent controls dies, you may pay {1}{B}. If you do, return that card to the battlefield under your control. If it's a creature, it's a Zombie in addition to its other creature types.\n{1}{B}: Regenerate target Zombie. Oracle:Whenever a creature an opponent controls dies, you may pay {1}{B}. If you do, return that card to the battlefield under your control. If it's a creature, it's a Zombie in addition to its other creature types.\n{1}{B}: Regenerate target Zombie.

View File

@@ -1,10 +1,11 @@
Name:Necromantic Selection Name:Necromantic Selection
ManaCost:4 B B B ManaCost:4 B B B
Types:Sorcery Types:Sorcery
A:SP$ DestroyAll | Cost$ 4 B B B | ValidCards$ Creature | RememberDestroyed$ True | SubAbility$ DBReturn | SpellDescription$ Destroy all creatures, then return a creature card put into a graveyard this way to the battlefield under your control. It's a black Zombie in addition to its other colors and types. Exile CARDNAME. A:SP$ DestroyAll | Cost$ 4 B B B | ValidCards$ Creature | RememberDestroyed$ True | SubAbility$ TrigImprint | SpellDescription$ Destroy all creatures, then return a creature card put into a graveyard this way to the battlefield under your control. It's a black Zombie in addition to its other colors and types. Exile CARDNAME.
SVar:DBReturn:DB$ ChangeZone | ChangeType$ Creature.nonToken+IsRemembered | ChangeNum$ 1 | Hidden$ True | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | RememberChanged$ True | ForgetOtherRemembered$ True | SubAbility$ DBZombify SVar:TrigImprint:DB$ Pump | ImprintCards$ Remembered | SubAbility$ DBClearRemember | StackDescription$ None
SVar:DBZombify:DB$ Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True | SubAbility$ DBCleanup SVar:DBClearRemember:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBReturn
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBChange SVar:DBReturn:DB$ ChangeZone | ChangeType$ Creature.nonToken+IsImprinted | ChangeNum$ 1 | Hidden$ True | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | AnimateSubAbility$ DBZombify | SubAbility$ DBCleanup
SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile SVar:DBZombify:DB$ Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/necromantic_selection.jpg SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True | SubAbility$ DBChange
SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile | StackDescription$ None
Oracle:Destroy all creatures, then return a creature card put into a graveyard this way to the battlefield under your control. It's a black Zombie in addition to its other colors and types. Exile Necromantic Selection. Oracle:Destroy all creatures, then return a creature card put into a graveyard this way to the battlefield under your control. It's a black Zombie in addition to its other colors and types. Exile Necromantic Selection.

View File

@@ -1,7 +1,6 @@
Name:Rise from the Grave Name:Rise from the Grave
ManaCost:4 B ManaCost:4 B
Types:Sorcery Types:Sorcery
A:SP$ ChangeZone | Cost$ 4 B | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | TgtPrompt$ Choose target creature card in a graveyard | ValidTgts$ Creature | SubAbility$ Animate | SpellDescription$ Put target creature card from a graveyard onto the battlefield under your control. That creature is a black Zombie in addition to its other colors and types. A:SP$ ChangeZone | Cost$ 4 B | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | TgtPrompt$ Choose target creature card in a graveyard | ValidTgts$ Creature | AnimateSubAbility$ Animate | SpellDescription$ Put target creature card from a graveyard onto the battlefield under your control. That creature is a black Zombie in addition to its other colors and types.
SVar:Animate:DB$Animate | Defined$ Targeted | Types$ Zombie | Colors$ Black | Permanent$ True SVar:Animate:DB$Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/rise_from_the_grave.jpg
Oracle:Put target creature card from a graveyard onto the battlefield under your control. That creature is a black Zombie in addition to its other colors and types. Oracle:Put target creature card from a graveyard onto the battlefield under your control. That creature is a black Zombie in addition to its other colors and types.

View File

@@ -5,7 +5,7 @@ Loyalty:4
S:Mode$ Continuous | Affected$ Creature.YouCtrl,Planeswalker.YouCtrl | AddKeyword$ Lifelink | Condition$ PlayerTurn | Description$ As long as it's your turn, creatures and planeswalkers you control have lifelink. S:Mode$ Continuous | Affected$ Creature.YouCtrl,Planeswalker.YouCtrl | AddKeyword$ Lifelink | Condition$ PlayerTurn | Description$ As long as it's your turn, creatures and planeswalkers you control have lifelink.
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
A:AB$ DealDamage | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | ValidTgts$ Player,Planeswalker | TgtPrompt$ Select target player or planeswalker | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to target player or planeswalker. A:AB$ DealDamage | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | ValidTgts$ Player,Planeswalker | TgtPrompt$ Select target player or planeswalker | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to target player or planeswalker.
A:AB$ ChangeZone | Cost$ SubCounter<X/LOYALTY> | Planeswalker$ True | Origin$ Graveyard | Destination$ Battlefield | References$ X | ValidTgts$ Creature.YouOwn | AILogic$ SorinVengefulBloodlord | TgtPrompt$ Select target creature with converted mana cost X from your graveyard | SubAbility$ Animate | SpellDescription$ Return target creature card with converted mana cost X from your graveyard to the battlefield. That creature is a Vampire in addition to its other types. A:AB$ ChangeZone | Cost$ SubCounter<X/LOYALTY> | Planeswalker$ True | Origin$ Graveyard | Destination$ Battlefield | References$ X | ValidTgts$ Creature.YouOwn | AILogic$ SorinVengefulBloodlord | TgtPrompt$ Select target creature with converted mana cost X from your graveyard | AnimateSubAbility$ Animate | SpellDescription$ Return target creature card with converted mana cost X from your graveyard to the battlefield. That creature is a Vampire in addition to its other types.
SVar:Animate:DB$ Animate | Defined$ Targeted | Types$ Vampire | Permanent$ True SVar:Animate:DB$ Animate | Defined$ Remembered | Types$ Vampire | Permanent$ True
SVar:X:Targeted$CardManaCost SVar:X:Targeted$CardManaCost
Oracle:As long as it's your turn, creatures and planeswalkers you control have lifelink.\n[+2]: Sorin, Vengeful Bloodlord deals 1 damage to target player or planeswalker.\n-X: Return target creature card with converted mana cost X from your graveyard to the battlefield. That creature is a Vampire in addition to its other types. Oracle:As long as it's your turn, creatures and planeswalkers you control have lifelink.\n[+2]: Sorin, Vengeful Bloodlord deals 1 damage to target player or planeswalker.\n-X: Return target creature card with converted mana cost X from your graveyard to the battlefield. That creature is a Vampire in addition to its other types.

View File

@@ -2,7 +2,7 @@ Name:Worms of the Earth
ManaCost:2 B B B ManaCost:2 B B B
Types:Enchantment Types:Enchantment
S:Mode$ CantPlayLand | Description$ Players can't play lands. S:Mode$ CantPlayLand | Description$ Players can't play lands.
S:Mode$ Continuous | EffectZone$ Battlefield | GlobalRule$ Lands can't enter the battlefield. | Description$ Lands can't enter the battlefield. R:Event$ Moved | ActiveZones$ Battlefield | Destination$ Battlefield | ValidCard$ Land | Prevent$ True | Description$ Lands can't enter the battlefield.
T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | Execute$ RepeatAbility | TriggerDescription$ At the beginning of each upkeep, any player may sacrifice two lands or have CARDNAME deal 5 damage to that player. If a player does either, destroy CARDNAME. T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | Execute$ RepeatAbility | TriggerDescription$ At the beginning of each upkeep, any player may sacrifice two lands or have CARDNAME deal 5 damage to that player. If a player does either, destroy CARDNAME.
SVar:RepeatAbility:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChoose SVar:RepeatAbility:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChoose
SVar:DBChoose:DB$ GenericChoice | Defined$ Player.IsRemembered | Choices$ SacTwoLands,DealDmg | AILogic$ PayUnlessCost SVar:DBChoose:DB$ GenericChoice | Defined$ Player.IsRemembered | Choices$ SacTwoLands,DealDmg | AILogic$ PayUnlessCost