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

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -57,7 +57,7 @@ import java.util.*;
/**
* Methods for common actions performed during a game.
*
*
* @author Forge
* @version $Id$
*/
@@ -80,7 +80,7 @@ public class GameAction {
public Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position, SpellAbility cause) {
return changeZone(zoneFrom, zoneTo, c, position, cause, null);
}
private Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position, SpellAbility cause, Map<AbilityKey, Object> params) {
if (c.isCopiedSpell() || (c.isImmutable() && zoneTo.is(ZoneType.Exile))) {
// Remove Effect from command immediately, this is essential when some replacement
@@ -125,11 +125,16 @@ public class GameAction {
Card copied = 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()) {
boolean resetToOriginal = false;
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
resetToOriginal = true;
}
@@ -164,8 +169,22 @@ public class GameAction {
// Don't copy Tokens, copy only cards leaving the battlefield
// and returning to hand (to recreate their spell ability information)
if (suppress || (!fromBattlefield && !toHand)) {
lastKnownInfo = 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 {
// if from Battlefield to Graveyard and Card does exist in LastStateBattlefield
// use that instead
@@ -189,15 +208,17 @@ public class GameAction {
}
if (!c.isToken()) {
if (c.isCloned() || c.hasTextChangeState()) {
c.removeCloneStates();
c.removeTextChangeStates();
boolean updateState = false;
updateState |= c.removeCloneStates();
updateState |= c.removeTextChangeStates();
if (updateState) {
c.updateStateForView();
}
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
// (we need to do this on the object before copying it, or it won't work correctly e.g.
// 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 (zoneFrom == null) {
copied.getOwner().addInboundToken(copied);
@@ -298,15 +266,29 @@ public class GameAction {
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams);
if (repres != ReplacementResult.NotReplaced) {
// reset failed manifested Cards back to original
if (c.isManifested()) {
if (c.isManifested() && !c.isInZone(ZoneType.Battlefield)) {
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);
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;
}
}
@@ -370,7 +352,7 @@ public class GameAction {
// do ETB counters after zone add
if (!suppress) {
if (toBattlefield ) {
if (toBattlefield) {
copied.putEtbCounters(table);
// enable replacement effects again
for (final ReplacementEffect re : copied.getReplacementEffects()) {
@@ -393,6 +375,14 @@ public class GameAction {
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
checkStaticAbilities();
game.getTriggerHandler().clearInstrinsicActiveTriggers(c, zoneFrom);
@@ -415,7 +405,7 @@ public class GameAction {
}
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);
runParams2.put(AbilityKey.OriginalController, zoneFrom.getPlayer());
if(params != null) {
@@ -447,7 +437,7 @@ public class GameAction {
copied.clearExploited();
}
// rule 504.6: reveal a face-down card leaving the stack
// rule 504.6: reveal a face-down card leaving the stack
if (zoneFrom != null && zoneTo != null && zoneFrom.is(ZoneType.Stack) && !zoneTo.is(ZoneType.Battlefield) && wasFacedown) {
Card revealLKI = CardUtil.getLKICopy(c);
revealLKI.turnFaceUp(true, false);
@@ -491,7 +481,7 @@ public class GameAction {
// Remove all changed keywords
copied.removeAllChangedText(game.getNextTimestamp());
} else if (toBattlefield) {
// reset timestamp in changezone effects so they have same timestamp if ETB simutaneously
// reset timestamp in changezone effects so they have same timestamp if ETB simutaneously
copied.setTimestamp(game.getNextTimestamp());
for (Player p : game.getPlayers()) {
copied.getDamageHistory().setNotAttackedSinceLastUpkeepOf(p);
@@ -540,7 +530,7 @@ public class GameAction {
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,
// use FThreads.invokeInNewThread to run code in a pooled thread
return moveTo(zoneTo, c, null, cause, params);
@@ -618,8 +608,12 @@ public class GameAction {
}
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();
return moveTo(stack, c, cause);
return moveTo(stack, c, cause, params);
}
public final Card moveToGraveyard(final Card c, SpellAbility cause) {
@@ -634,7 +628,7 @@ public class GameAction {
public final Card moveToHand(final Card c, SpellAbility cause) {
return moveToHand(c, cause, null);
}
public final Card moveToHand(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
final PlayerZone hand = c.getOwner().getZone(ZoneType.Hand);
return moveTo(hand, c, cause, params);
@@ -648,7 +642,7 @@ public class GameAction {
public final Card moveToPlay(final Card c, final Player p, SpellAbility cause) {
return moveToPlay(c, p, cause, null);
}
public final Card moveToPlay(final Card c, final Player p, SpellAbility cause, Map<AbilityKey, Object> params) {
// move to a specific player's Battlefield
final PlayerZone play = p.getZone(ZoneType.Battlefield);
@@ -658,7 +652,7 @@ public class GameAction {
public final Card moveToBottomOfLibrary(final Card c, SpellAbility cause) {
return moveToBottomOfLibrary(c, cause, null);
}
public final Card moveToBottomOfLibrary(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
return moveToLibrary(c, -1, cause, params);
}
@@ -666,7 +660,7 @@ public class GameAction {
public final Card moveToLibrary(final Card c, SpellAbility cause) {
return moveToLibrary(c, cause, null);
}
public final Card moveToLibrary(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
return moveToLibrary(c, 0, cause, params);
}
@@ -674,7 +668,7 @@ public class GameAction {
public final Card moveToLibrary(Card c, int libPosition, SpellAbility cause) {
return moveToLibrary(c, libPosition, cause, null);
}
public final Card moveToLibrary(Card c, int libPosition, SpellAbility cause, Map<AbilityKey, Object> params) {
final PlayerZone library = c.getOwner().getZone(ZoneType.Library);
if (libPosition == -1 || libPosition > library.size()) {
@@ -684,11 +678,15 @@ public class GameAction {
}
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);
if (deckPosition == -1 || 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) {
@@ -723,16 +721,20 @@ public class GameAction {
}
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
switch(name) {
case Hand: return moveToHand(c, cause);
case Library: return moveToLibrary(c, libPosition, cause);
case Battlefield: return moveToPlay(c, cause);
case Graveyard: return moveToGraveyard(c, cause);
case Exile: return exile(c, cause);
case Stack: return moveToStack(c, cause);
case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause);
case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause);
case Hand: return moveToHand(c, cause, params);
case Library: return moveToLibrary(c, libPosition, cause, params);
case Battlefield: return moveToPlay(c, c.getController(), cause, params);
case Graveyard: return moveToGraveyard(c, cause, params);
case Exile: return exile(c, cause, params);
case Stack: return moveToStack(c, cause, params);
case PlanarDeck: return moveToVariantDeck(c, ZoneType.PlanarDeck, libPosition, cause, params);
case SchemeDeck: return moveToVariantDeck(c, ZoneType.SchemeDeck, libPosition, cause, params);
default: // sideboard will also get there
return moveTo(c.getOwner().getZone(name), c, cause);
}
@@ -793,7 +795,7 @@ public class GameAction {
}
});
final Comparator<StaticAbility> comp = new Comparator<StaticAbility>() {
@Override
public int compare(final StaticAbility a, final StaticAbility b) {
@@ -1073,7 +1075,7 @@ public class GameAction {
if (!game.isGameOver()) {
checkGameOverCondition();
}
if (game.getAge() != GameStage.Play) {
return;
}

View File

@@ -34,8 +34,7 @@ public enum GlobalRuleChange {
onlyOneBlockerPerOpponent ("Each opponent can't block with more than one creature."),
onlyTwoBlockers ("No more than two creatures can block each combat."),
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."),
noLandBattlefield("Lands can't enter the battlefield.");
blankIsChaos("Each blank roll of the planar dice is a {CHAOS} roll.");
private final String ruleText;

View File

@@ -54,7 +54,8 @@ public final class AbilityFactory {
"Execute", // DelayedTrigger
"FallbackAbility", // Complex Unless costs which can be unpayable
"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 {

View File

@@ -1,6 +1,8 @@
package forge.game.ability.effects;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.GameActionUtil;
import forge.game.ability.AbilityKey;
@@ -92,7 +94,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
sa.getActivatingPlayer().getController().reveal(handCards, ZoneType.Hand, handCards.get(0).getOwner());
}
}
cards = (CardCollection)AbilityUtils.filterListByType(cards, sa.getParam("ChangeType"), sa);
if (sa.hasParam("Optional")) {
@@ -150,7 +152,19 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
}
}
Map<AbilityKey, Object> moveParams = Maps.newEnumMap(AbilityKey.class);
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
if (c.isAura()) {
final SpellAbility saAura = c.getFirstAttachSpell();
@@ -165,9 +179,9 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
Card movedCard = null;
if (sa.hasParam("GainControl")) {
c.setController(sa.getActivatingPlayer(), game.getNextTimestamp());
movedCard = game.getAction().moveToPlay(c, sa.getActivatingPlayer(), sa);
movedCard = game.getAction().moveToPlay(c, sa.getActivatingPlayer(), sa, moveParams);
} else {
movedCard = game.getAction().moveTo(destination, c, libraryPos, sa);
movedCard = game.getAction().moveTo(destination, c, libraryPos, sa, moveParams);
if (destination == ZoneType.Exile && !c.isToken()) {
Card host = sa.getOriginalHost();
if (host == null) {
@@ -205,7 +219,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
if (destination == ZoneType.Battlefield) {
movedCard.setTimestamp(ts);
}
if (!movedCard.getZone().equals(originZone)) {
triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard);
}

View File

@@ -4,6 +4,8 @@ import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.GameCommand;
import forge.card.CardStateName;
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
// location
if (tgtC.isAura()) {
@@ -530,13 +543,16 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (saAura != null) {
saAura.setActivatingPlayer(sa.getActivatingPlayer());
if (!saAura.getTargetRestrictions().hasCandidates(saAura, false)) {
if (sa.hasAdditionalAbility("AnimateSubAbility")) {
tgtC.removeChangedState();
}
continue;
}
}
}
movedCard = game.getAction().moveTo(
tgtC.getController().getZone(destination), tgtC, sa);
tgtC.getController().getZone(destination), tgtC, sa, moveParams);
if (sa.hasParam("Unearth")) {
movedCard.setUnearthed(true);
movedCard.addChangedCardKeywords(Lists.newArrayList("Haste"), null, false, false,
@@ -983,6 +999,18 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (sa.hasParam("Tapped")) {
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")) {
Player newController = sa.getActivatingPlayer();
if (sa.hasParam("NewController")) {
@@ -1109,7 +1137,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
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")) {
movedCard.setTapped(true);
}

View File

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

View File

@@ -1,15 +1,11 @@
package forge.game.ability.effects;
import com.google.common.collect.Sets;
import forge.game.Game;
import forge.game.GlobalRuleChange;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CardUtil;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
@@ -56,20 +52,8 @@ public class ManifestEffect extends SpellAbilityEffect {
}
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);
if (sa.hasParam("RememberManifested") && rem != null) {
if (sa.hasParam("RememberManifested") && rem != null && rem.isManifested()) {
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);
// Add manifest demorph static ability for creatures
if (isCreature && !cost.isNoCost()) {
if (c.isManifested() && isCreature && !cost.isNoCost()) {
// Add Manifest to original State
c.getState(CardStateName.Original).addSpellAbility(CardFactoryUtil.abilityManifestFaceUp(c, cost));
c.updateStateForView();
@@ -1168,10 +1168,10 @@ public class Card extends GameEntity implements Comparable<Card> {
return mustAttackEntity;
}
public final void clearMustAttackEntity(final Player playerturn) {
if (getController().equals(playerturn)) {
mustAttackEntity = null;
}
mustAttackEntityThisTurn = null;
if (getController().equals(playerturn)) {
mustAttackEntity = null;
}
mustAttackEntityThisTurn = null;
}
public final GameEntity getMustAttackEntityThisTurn() { return mustAttackEntityThisTurn; }
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) {
if (sta == null) {
return null;
}
return mayPlay.get(sta);
if (sta == null) {
return null;
}
return mayPlay.get(sta);
}
public final List<CardPlayOption> mayPlay(final Player player) {
@@ -3078,6 +3078,27 @@ public class Card extends GameEntity implements Comparable<Card> {
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() {
return changedCardKeywords;
}
@@ -3281,8 +3302,13 @@ public class Card extends GameEntity implements Comparable<Card> {
return clStates.getHost();
}
public final void removeCloneStates() {
public final boolean removeCloneStates() {
if (clonedStates.isEmpty()) {
return false;
}
clonedStates.clear();
updateCloneState(false);
return true;
}
public final Map<Long, CardCloneStates> getCloneStates() {
@@ -3334,8 +3360,13 @@ public class Card extends GameEntity implements Comparable<Card> {
}
return false;
}
public final void removeTextChangeStates() {
public final boolean removeTextChangeStates() {
if (textChangeStates.isEmpty()) {
return false;
}
textChangeStates.clear();
updateCloneState(false);
return true;
}
private final CardCloneStates getLastTextChangeState() {
@@ -3502,8 +3533,8 @@ public class Card extends GameEntity implements Comparable<Card> {
}
public final boolean toughnessAssignsDamage() {
return getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.toughnessAssignsDamage)
|| hasKeyword("CARDNAME assigns combat damage equal to its toughness rather than its power");
return getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.toughnessAssignsDamage)
|| hasKeyword("CARDNAME assigns combat damage equal to its toughness rather than its power");
}
// 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...
public final List<KeywordInterface> getKeywords() {
return getKeywords(currentState);
@@ -3687,7 +3726,7 @@ public class Card extends GameEntity implements Comparable<Card> {
}
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,
@@ -4694,8 +4733,8 @@ public class Card extends GameEntity implements Comparable<Card> {
public final boolean hasDealtDamageToOpponentThisTurn() {
for (final GameEntity e : getDamageHistory().getThisTurnDamaged()) {
if (e instanceof Player) {
final Player p = (Player) e;
if (e instanceof Player) {
final Player p = (Player) e;
if (getController().isOpponentOf(p)) {
return true;
}
@@ -5132,7 +5171,7 @@ public class Card extends GameEntity implements Comparable<Card> {
return eternalized;
}
public final void setEternalized(final boolean b) {
eternalized = b;
eternalized = b;
}
public final int getExertedThisTurn() {
@@ -5835,10 +5874,10 @@ public class Card extends GameEntity implements Comparable<Card> {
continue;
}
if (drawbackOnly && params.containsKey("Execute")){
String exec = this.getSVar(params.get("Execute"));
if (exec.contains("AB$")) {
continue;
}
String exec = this.getSVar(params.get("Execute"));
if (exec.contains("AB$")) {
continue;
}
}
return true;
}
@@ -6482,4 +6521,22 @@ public class Card extends GameEntity implements Comparable<Card> {
public boolean canBlockAny() {
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);
affectedLKI = CardUtil.getLKICopy(affectedCard);
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);
game.getAction().checkStaticAbilities(false, Sets.newHashSet(affectedLKI), preList);
checkAgain = true;

View File

@@ -2121,6 +2121,43 @@ public class GameSimulatorTest extends SimulationTestCase {
assertEquals(1, simWC.getPowerBonusFromCounters());
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
PT:3/3
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.
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
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:DBReturn:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Battlefield | AnimateSubAbility$ DBAnimate
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: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.

View File

@@ -3,8 +3,8 @@ ManaCost:3 B B
Types:Legendary Creature Human Minion
PT:3/3
S:Mode$ Continuous | Affected$ Creature.Nightmare | AddPower$ 1 | AddToughness$ 1 | Description$ Nightmare creatures get +1/+1.
A:AB$ ChangeZone | Cost$ B B B PayLife<3> | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | TgtPrompt$ Select target creature card in a graveyard | ValidTgts$ Creature | ChangeNum$ 1 | 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.
SVar:DBAnimate:DB$Animate | Defined$ Targeted | Types$ Nightmare | Colors$ Black | Permanent$ True | OverwriteColors$ True
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
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigExile | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, exile all Nightmares.
SVar:TrigExile:DB$ ChangeZoneAll | Origin$ Battlefield | Destination$ Exile | ChangeType$ Nightmare
SVar:PlayMain1:TRUE

View File

@@ -1,8 +1,8 @@
Name:Dance of the Manse
ManaCost:X W U
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.
SVar:DBAnimate:DB$ Animate | Defined$ Targeted | Types$ Creature | Power$ 4 | Toughness$ 4 | Permanent$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ GE6 | References$ X
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$ Remembered | Types$ Creature | Power$ 4 | Toughness$ 4 | Permanent$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ GE6 | References$ X
SVar:X:Count$xPaid
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.

View File

@@ -3,8 +3,6 @@ ManaCost:3 B B
Types:Creature Zombie Horror
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.
SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | Defined$ TriggeredCard | RememberChanged$ True | SubAbility$ DBAnimate
SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/dread_slaver.jpg
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
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
ManaCost:4 B B
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.
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.
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$ 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.
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
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
ManaCost:1
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$ Library | Destination$ Battlefield | ValidCard$ Creature | Prevent$ True
S:Mode$ CantBeCast | Origin$ Graveyard | Description$ Players can't cast spells from graveyards or libraries.
S:Mode$ CantBeCast | Origin$ Library
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.
S:Mode$ CantBeCast | Origin$ Graveyard,Library | Description$ Players can't cast spells from graveyards or libraries.
SVar:NonStackingEffect:True
AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/grafdiggers_cage.jpg

View File

@@ -1,12 +1,9 @@
Name:Grave Betrayal
ManaCost:5 B B
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.
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: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:GBReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | RememberChanged$ True | ForgetOtherRemembered$ True | SubAbility$ GBCounter
SVar:GBCounter:DB$ PutCounter | Defined$ Remembered | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ GBZombify
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
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: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:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:GBReturn:DB$ ChangeZone | Defined$ DelayTriggerRemembered | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | WithCounters$ P1P1_1 | AnimateSubAbility$ GBZombify
SVar:GBZombify:DB$ Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True
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
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$ 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.
SVar:DBAnimate:DB$ AnimateAll | ValidCards$ Creature.IsRemembered | Colors$ Black | Types$ Zombie | Permanent$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True
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$ Animate | Defined$ Remembered | Colors$ Black | Types$ Zombie | Permanent$ True
AI:RemoveDeck:All
SVar:IsReanimatorCard:TRUE
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:Menace
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.
SVar:NonStackingEffect:True
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
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
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.
SVar:Animate:DB$Animate | Defined$ Targeted | Types$ Zombie | Colors$ Black | Permanent$ True
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$ 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.
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.

View File

@@ -3,9 +3,8 @@ ManaCost:5 B B
Types:Legendary Creature Human Wizard
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.
SVar:TrigReturn:AB$ ChangeZone | Cost$ 1 B | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | Defined$ TriggeredCard | RememberChanged$ True | SubAbility$ Animate
SVar:Animate:DB$ Animate | Defined$ Remembered | Types$ Zombie | Permanent$ True | ConditionDefined$ Remembered | ConditionPresent$ Creature | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
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
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
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
ManaCost:4 B B B
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.
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:DBZombify:DB$ Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBChange
SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile
SVar:Picture:http://www.wizards.com/global/images/magic/general/necromantic_selection.jpg
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:TrigImprint:DB$ Pump | ImprintCards$ Remembered | SubAbility$ DBClearRemember | StackDescription$ None
SVar:DBClearRemember:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBReturn
SVar:DBReturn:DB$ ChangeZone | ChangeType$ Creature.nonToken+IsImprinted | ChangeNum$ 1 | Hidden$ True | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | AnimateSubAbility$ DBZombify | SubAbility$ DBCleanup
SVar:DBZombify:DB$ Animate | Defined$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True
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.

View File

@@ -1,7 +1,6 @@
Name:Rise from the Grave
ManaCost:4 B
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.
SVar:Animate:DB$Animate | Defined$ Targeted | Types$ Zombie | Colors$ Black | Permanent$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/rise_from_the_grave.jpg
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$ Remembered | Types$ Zombie | Colors$ Black | Permanent$ True
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.
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$ 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.
SVar:Animate:DB$ Animate | Defined$ Targeted | Types$ Vampire | Permanent$ True
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$ Remembered | Types$ Vampire | Permanent$ True
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.

View File

@@ -2,7 +2,7 @@ Name:Worms of the Earth
ManaCost:2 B B B
Types:Enchantment
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.
SVar:RepeatAbility:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChoose
SVar:DBChoose:DB$ GenericChoice | Defined$ Player.IsRemembered | Choices$ SacTwoLands,DealDmg | AILogic$ PayUnlessCost