Fix Crawling Sensation + related tweaks (#4744)

* Fix Foretell rollback

* Fix Crawling Sensation

* Stonebinder's Familiar should trigger when Time Stop exiles spell card

* Support Leave GY LKI

* Tweak logic so it only reuses the table when simultaneous

* Fix NPE

* AI fix

* Fix countered spell not exiled by Dauthi Voidwalker

* Fix Parallax Wave not returning when exiling itself

* Fix LKI update timing

---------

Co-authored-by: TRT <>
Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.60>
This commit is contained in:
tool4ever
2024-02-27 15:33:13 +01:00
committed by GitHub
parent 34bd50f222
commit dcbe9c4ba1
22 changed files with 96 additions and 58 deletions

View File

@@ -168,7 +168,7 @@ public class FightAi extends SpellAbilityAi {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa); final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
AbilitySub tgtFight = sa.getSubAbility(); AbilitySub tgtFight = sa.getSubAbility();
while (tgtFight != null && tgtFight.getApi() != ApiType.Fight && tgtFight.getApi() != ApiType.DealDamage) { while (tgtFight != null && tgtFight.getApi() != ApiType.Fight && tgtFight.getApi() != ApiType.DealDamage && tgtFight.getApi() != ApiType.EachDamage) {
// Search for the Fight/DealDamage subability (matters e.g. for Ent's Fury where the Fight SA is not an immediate child of Pump) // Search for the Fight/DealDamage subability (matters e.g. for Ent's Fury where the Fight SA is not an immediate child of Pump)
tgtFight = tgtFight.getSubAbility(); tgtFight = tgtFight.getSubAbility();
} }

View File

@@ -1252,11 +1252,12 @@ public class GameAction {
boolean orderedDesCreats = false; boolean orderedDesCreats = false;
boolean orderedNoRegCreats = false; boolean orderedNoRegCreats = false;
boolean orderedSacrificeList = false; boolean orderedSacrificeList = false;
CardCollection cardsToUpdateLKI = new CardCollection();
for (int q = 0; q < 9; q++) { for (int q = 0; q < 9; q++) {
checkStaticAbilities(false, affectedCards, CardCollection.EMPTY);
boolean checkAgain = false; boolean checkAgain = false;
CardCollection cardsToUpdateLKI = new CardCollection();
checkStaticAbilities(false, affectedCards, CardCollection.EMPTY);
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard()); CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
Map<AbilityKey, Object> mapParams = AbilityKey.newMap(); Map<AbilityKey, Object> mapParams = AbilityKey.newMap();
@@ -1445,10 +1446,14 @@ public class GameAction {
table.triggerChangesZoneAll(game, null); table.triggerChangesZoneAll(game, null);
if (!checkAgain) { for (final Card c : cardsToUpdateLKI) {
break; // do not continue the loop game.updateLastStateForCard(c);
} else { }
if (checkAgain) {
performedSBA = true; performedSBA = true;
} else {
break; // do not continue the loop
} }
} // for q=0;q<9 } // for q=0;q<9
@@ -1473,10 +1478,6 @@ public class GameAction {
// this point. // this point.
checkStaticAbilities(false, affectedCards, CardCollection.EMPTY); checkStaticAbilities(false, affectedCards, CardCollection.EMPTY);
for (final Card c : cardsToUpdateLKI) {
game.updateLastStateForCard(c);
}
if (!refreeze) { if (!refreeze) {
game.getStack().unfreezeStack(); game.getStack().unfreezeStack();
} }

View File

@@ -879,7 +879,7 @@ public final class GameActionUtil {
oldCard.getZone().remove(oldCard); oldCard.getZone().remove(oldCard);
// in some rare cases the old position no longer exists (Panglacial Wurm + Selvala) // in some rare cases the old position no longer exists (Panglacial Wurm + Selvala)
Integer newPosition = zonePosition >= 0 ? Math.min(Integer.valueOf(zonePosition), fromZone.size()) : null; Integer newPosition = zonePosition >= 0 ? Math.min(Integer.valueOf(zonePosition), fromZone.size()) : null;
fromZone.add(oldCard, newPosition); fromZone.add(oldCard, newPosition, null, true);
ability.setHostCard(oldCard); ability.setHostCard(oldCard);
ability.setXManaCostPaid(null); ability.setXManaCostPaid(null);
ability.setSpendPhyrexianMana(false); ability.setSpendPhyrexianMana(false);

View File

@@ -1,9 +1,11 @@
package forge.game.ability; package forge.game.ability;
import forge.game.Game;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardZoneTable; import forge.game.card.CardZoneTable;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.Map; import java.util.Map;
@@ -210,9 +212,14 @@ public enum AbilityKey {
map.put(AbilityKey.LastStateGraveyard, table.getLastStateGraveyard()); map.put(AbilityKey.LastStateGraveyard, table.getLastStateGraveyard());
map.put(AbilityKey.InternalTriggerTable, table); map.put(AbilityKey.InternalTriggerTable, table);
} }
public static CardZoneTable addCardZoneTableParams(Map<AbilityKey, Object> map, forge.game.spellability.SpellAbility sa) { public static CardZoneTable addCardZoneTableParams(Map<AbilityKey, Object> map, SpellAbility sa) {
CardZoneTable table = new CardZoneTable(sa.getLastStateBattlefield(), sa.getLastStateGraveyard()); CardZoneTable table = new CardZoneTable(sa.getLastStateBattlefield(), sa.getLastStateGraveyard());
addCardZoneTableParams(map, table); addCardZoneTableParams(map, table);
return table; return table;
} }
public static CardZoneTable addCardZoneTableParams(Map<AbilityKey, Object> map, Game game) {
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
addCardZoneTableParams(map, table);
return table;
}
} }

View File

@@ -1595,9 +1595,6 @@ public class AbilityUtils {
} }
host.addRemembered(sb.toString()); host.addRemembered(sb.toString());
} }
// make sure that when this is from a trigger LKI is updated
host.getGame().updateLastStateForCard(host);
} }
/** /**

View File

@@ -17,6 +17,7 @@ import forge.game.player.PlayerCollection;
import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementHandler; import forge.game.replacement.ReplacementHandler;
import forge.game.replacement.ReplacementLayer; import forge.game.replacement.ReplacementLayer;
import forge.game.replacement.ReplacementType;
import forge.game.spellability.AbilitySub; import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
@@ -945,11 +946,13 @@ public abstract class SpellAbilityEffect {
} }
} }
public static void handleExiledWith(final Card movedCard, final SpellAbility cause) { public static void handleExiledWith(final Card movedCard, final SpellAbility cause) {
handleExiledWith(movedCard, cause, cause.getHostCard());
}
public static void handleExiledWith(final Card movedCard, final SpellAbility cause, Card exilingSource) {
if (movedCard.isToken()) { if (movedCard.isToken()) {
return; return;
} }
Card exilingSource = cause.getHostCard();
// during replacement LKI might be used // during replacement LKI might be used
if (cause.isReplacementAbility() && exilingSource.isLKI()) { if (cause.isReplacementAbility() && exilingSource.isLKI()) {
exilingSource = exilingSource.getGame().getCardState(exilingSource); exilingSource = exilingSource.getGame().getCardState(exilingSource);
@@ -969,7 +972,9 @@ public abstract class SpellAbilityEffect {
} }
public CardZoneTable getChangeZoneTable(SpellAbility sa, CardCollectionView lastStateBattlefield, CardCollectionView lastStateGraveyard) { public CardZoneTable getChangeZoneTable(SpellAbility sa, CardCollectionView lastStateBattlefield, CardCollectionView lastStateGraveyard) {
if (sa.isReplacementAbility() && sa.getReplacingObject(AbilityKey.InternalTriggerTable) != null) { if (sa.isReplacementAbility() && sa.getReplacementEffect().getMode() == ReplacementType.Moved
&& sa.getReplacingObject(AbilityKey.InternalTriggerTable) != null) {
// if a RE changes the destination zone try to make it simultaneous
return (CardZoneTable) sa.getReplacingObject(AbilityKey.InternalTriggerTable); return (CardZoneTable) sa.getReplacingObject(AbilityKey.InternalTriggerTable);
} }
return new CardZoneTable(lastStateBattlefield, lastStateGraveyard); return new CardZoneTable(lastStateBattlefield, lastStateGraveyard);

View File

@@ -710,6 +710,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
movedCard = game.getAction().moveTo(destination, gameCard, sa, moveParams); movedCard = game.getAction().moveTo(destination, gameCard, sa, moveParams);
if (destination.equals(ZoneType.Exile) && lastStateBattlefield.contains(gameCard) && hostCard.equals(gameCard)) {
// support Parallax Wave returning itself
handleExiledWith(movedCard, sa, lastStateBattlefield.get(gameCard));
}
if (ZoneType.Hand.equals(destination) && ZoneType.Command.equals(originZone.getZoneType())) { if (ZoneType.Hand.equals(destination) && ZoneType.Command.equals(originZone.getZoneType())) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(movedCard.getName()).append(" has moved from Command Zone to ").append(player).append("'s hand."); sb.append(movedCard.getName()).append(" has moved from Command Zone to ").append(player).append("'s hand.");

View File

@@ -68,7 +68,7 @@ public class ConniveEffect extends SpellAbilityEffect {
CardCollection connivers = CardLists.filterControlledBy(toConnive, p); CardCollection connivers = CardLists.filterControlledBy(toConnive, p);
while (!connivers.isEmpty()) { while (!connivers.isEmpty()) {
GameEntityCounterTable table = new GameEntityCounterTable(); GameEntityCounterTable table = new GameEntityCounterTable();
final CardZoneTable triggerList = new CardZoneTable(sa.getLastStateBattlefield(), sa.getLastStateGraveyard()); final CardZoneTable triggerList = new CardZoneTable(game.copyLastStateBattlefield(), game.copyLastStateGraveyard());
Map<Player, CardCollectionView> discardedMap = Maps.newHashMap(); Map<Player, CardCollectionView> discardedMap = Maps.newHashMap();
Map<AbilityKey, Object> moveParams = AbilityKey.newMap(); Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
AbilityKey.addCardZoneTableParams(moveParams, triggerList); AbilityKey.addCardZoneTableParams(moveParams, triggerList);

View File

@@ -53,7 +53,7 @@ public class CounterEffect extends SpellAbilityEffect {
public void resolve(SpellAbility sa) { public void resolve(SpellAbility sa) {
final Game game = sa.getActivatingPlayer().getGame(); final Game game = sa.getActivatingPlayer().getGame();
Map<AbilityKey, Object> params = AbilityKey.newMap(); Map<AbilityKey, Object> params = AbilityKey.newMap();
final CardZoneTable table = AbilityKey.addCardZoneTableParams(params, sa); final CardZoneTable table = AbilityKey.addCardZoneTableParams(params, game);
for (final SpellAbility tgtSA : getTargetSpells(sa)) { for (final SpellAbility tgtSA : getTargetSpells(sa)) {
final Card tgtSACard = tgtSA.getHostCard(); final Card tgtSACard = tgtSA.getHostCard();

View File

@@ -25,6 +25,9 @@ public class EndCombatPhaseEffect extends SpellAbilityEffect {
return; return;
} }
// CR 721.2a
game.getTriggerHandler().clearWaitingTriggers();
// 1) All spells and abilities on the stack are exiled. // 1) All spells and abilities on the stack are exiled.
Map<AbilityKey, Object> moveParams = AbilityKey.newMap(); Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
CardZoneTable table = new CardZoneTable(sa.getLastStateBattlefield(), sa.getLastStateGraveyard()); CardZoneTable table = new CardZoneTable(sa.getLastStateBattlefield(), sa.getLastStateGraveyard());
@@ -35,7 +38,6 @@ public class EndCombatPhaseEffect extends SpellAbilityEffect {
game.getStack().clear(); game.getStack().clear();
game.getStack().clearSimultaneousStack(); game.getStack().clearSimultaneousStack();
game.getTriggerHandler().clearWaitingTriggers();
// 2) State-based actions are checked. No player gets priority, and no // 2) State-based actions are checked. No player gets priority, and no
// triggered abilities are put onto the stack. // triggered abilities are put onto the stack.
@@ -43,7 +45,6 @@ public class EndCombatPhaseEffect extends SpellAbilityEffect {
// 3) The current phase and step ends. The game skips straight to the postcombat main phase. As this happens, // 3) The current phase and step ends. The game skips straight to the postcombat main phase. As this happens,
// all attacking and blocking creatures are removed from combat and effects that last “until end of combat” expire. // all attacking and blocking creatures are removed from combat and effects that last “until end of combat” expire.
game.getPhaseHandler().endCombat();
game.getPhaseHandler().endCombatPhaseByEffect(); game.getPhaseHandler().endCombatPhaseByEffect();
} }

View File

@@ -28,7 +28,11 @@ public class EndTurnEffect extends SpellAbilityEffect {
if (sa.hasParam("Optional") && !ender.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantEndTurn"), null)) { if (sa.hasParam("Optional") && !ender.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantEndTurn"), null)) {
return; return;
} }
Game game = ender.getGame(); Game game = ender.getGame();
// CR 721.1a
game.getTriggerHandler().clearWaitingTriggers();
// Steps taken from gatherer's rulings on Time Stop. // Steps taken from gatherer's rulings on Time Stop.
// 1) All spells and abilities on the stack are exiled. This includes // 1) All spells and abilities on the stack are exiled. This includes
// Time Stop, though it will continue to resolve. It also includes // Time Stop, though it will continue to resolve. It also includes
@@ -42,7 +46,6 @@ public class EndTurnEffect extends SpellAbilityEffect {
game.getStack().clear(); game.getStack().clear();
game.getStack().clearSimultaneousStack(); game.getStack().clearSimultaneousStack();
game.getTriggerHandler().clearWaitingTriggers();
// 2) All attacking and blocking creatures are removed from combat. // 2) All attacking and blocking creatures are removed from combat.
game.getPhaseHandler().endCombat(); game.getPhaseHandler().endCombat();

View File

@@ -30,7 +30,6 @@ public class HauntEffect extends SpellAbilityEffect {
final Card copy = game.getAction().exile(card, sa, moveParams); final Card copy = game.getAction().exile(card, sa, moveParams);
sa.getTargetCard().addHauntedBy(copy); sa.getTargetCard().addHauntedBy(copy);
table.triggerChangesZoneAll(game, sa); table.triggerChangesZoneAll(game, sa);
} else if (!sa.usesTargeting() && card.getHaunting() != null) { } else if (!sa.usesTargeting() && card.getHaunting() != null) {
// unhaunt // unhaunt
card.getHaunting().removeHauntedBy(card); card.getHaunting().removeHauntedBy(card);

View File

@@ -1899,7 +1899,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
public final void cleanupExiledWith() { public final void cleanupExiledWith() {
if (exiledWith == null) { if (exiledWith == null || exiledWith.isLKI()) {
return; return;
} }

View File

@@ -13,7 +13,6 @@ import forge.game.CardTraitBase;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.player.PlayerCollection; import forge.game.player.PlayerCollection;
import forge.game.replacement.ReplacementType;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType; import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -85,7 +84,7 @@ public class CardZoneTable extends ForwardingTable<ZoneType, ZoneType, CardColle
public void triggerChangesZoneAll(final Game game, final SpellAbility cause) { public void triggerChangesZoneAll(final Game game, final SpellAbility cause) {
triggerTokenCreatedOnce(game); triggerTokenCreatedOnce(game);
if (cause != null && cause.isReplacementAbility() && cause.getReplacementEffect().getMode() == ReplacementType.Moved) { if (cause != null && cause.getReplacingObject(AbilityKey.InternalTriggerTable) == this) {
// will be handled by original "cause" instead // will be handled by original "cause" instead
return; return;
} }
@@ -113,21 +112,25 @@ public class CardZoneTable extends ForwardingTable<ZoneType, ZoneType, CardColle
public CardCollection filterCards(Iterable<ZoneType> origin, ZoneType destination, String valid, Card host, CardTraitBase sa) { public CardCollection filterCards(Iterable<ZoneType> origin, ZoneType destination, String valid, Card host, CardTraitBase sa) {
CardCollection allCards = new CardCollection(); CardCollection allCards = new CardCollection();
if (destination != null) { if (destination != null && !containsColumn(destination)) {
if (!containsColumn(destination)) { return allCards;
return allCards;
}
} }
if (origin != null) { if (origin != null) {
for (ZoneType z : origin) { for (ZoneType z : origin) {
CardCollectionView lkiLookup = CardCollection.EMPTY;
if (z == ZoneType.Battlefield) {
lkiLookup = lastStateBattlefield;
}
if (containsRow(z)) { if (containsRow(z)) {
CardCollectionView lkiLookup = CardCollection.EMPTY;
// CR 603.10a
if (z == ZoneType.Battlefield) {
lkiLookup = lastStateBattlefield;
}
if (z == ZoneType.Graveyard && destination == null) {
lkiLookup = lastStateGraveyard;
}
if (destination != null) { if (destination != null) {
for (Card c : row(z).get(destination)) { if (row(z).containsKey(destination)) {
allCards.add(lkiLookup.get(c)); for (Card c : row(z).get(destination)) {
allCards.add(lkiLookup.get(c));
}
} }
} else { } else {
for (CardCollection cc : row(z).values()) { for (CardCollection cc : row(z).values()) {

View File

@@ -1235,6 +1235,7 @@ public class PhaseHandler implements java.io.Serializable {
} }
public final void endCombatPhaseByEffect() { public final void endCombatPhaseByEffect() {
endCombat();
game.getAction().checkStateEffects(true); game.getAction().checkStateEffects(true);
setPhase(PhaseType.COMBAT_END); setPhase(PhaseType.COMBAT_END);
advanceToNextPhase(); advanceToNextPhase();
@@ -1243,6 +1244,7 @@ public class PhaseHandler implements java.io.Serializable {
public final void endTurnByEffect() { public final void endTurnByEffect() {
extraPhases.clear(); extraPhases.clear();
setPhase(PhaseType.CLEANUP); setPhase(PhaseType.CLEANUP);
game.fireEvent(new GameEventTurnPhase(playerTurn, phase, ""));
onPhaseBegin(); onPhaseBegin();
} }

View File

@@ -9,6 +9,7 @@ import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
import forge.game.card.CardUtil;
import forge.game.card.CardZoneTable; import forge.game.card.CardZoneTable;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -31,6 +32,15 @@ public class TriggerChangesZoneAll extends Trigger {
return false; return false;
} }
if (hasParam("FirstTime")) {
// currently only for Crawling Sensation
List<Card> entered = CardUtil.getThisTurnEntered(ZoneType.smartValueOf(getParam("Destination")), null, getParam("ValidCards"), getHostCard(), this, getHostCard().getController());
entered.removeAll(filterCards(table));
if (!entered.isEmpty()) {
return false;
}
}
if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) { if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
return false; return false;
} }

View File

@@ -85,6 +85,9 @@ public class Zone implements java.io.Serializable, Iterable<Card> {
add(c, index, null); add(c, index, null);
} }
public void add(final Card c, Integer index, final Card latestState) { public void add(final Card c, Integer index, final Card latestState) {
add(c, index, latestState, false);
}
public void add(final Card c, Integer index, final Card latestState, final boolean rollback) {
if (index != null && cardList.isEmpty() && index.intValue() > 0) { if (index != null && cardList.isEmpty() && index.intValue() > 0) {
// something went wrong, most likely the method fired when the game was in an unexpected state // something went wrong, most likely the method fired when the game was in an unexpected state
// (e.g. conceding during the mana payment prompt) // (e.g. conceding during the mana payment prompt)
@@ -101,28 +104,30 @@ public class Zone implements java.io.Serializable, Iterable<Card> {
} }
} }
// Immutable cards are usually emblems and effects if (!rollback) {
if (!c.isImmutable()) { // Immutable cards are usually emblems and effects
final Zone oldZone = game.getZoneOf(c); if (!c.isImmutable()) {
final ZoneType zt = oldZone == null ? ZoneType.Stack : oldZone.getZoneType(); final Zone oldZone = game.getZoneOf(c);
final ZoneType zt = oldZone == null ? ZoneType.Stack : oldZone.getZoneType();
// only if the zoneType differs from this // only if the zoneType differs from this
// don't go in there is its a control change // don't go in there is its a control change
if (zt != zoneType) { if (zt != zoneType) {
c.setTurnInController(getPlayer()); c.setTurnInController(getPlayer());
c.setTurnInZone(game.getPhaseHandler().getTurn()); c.setTurnInZone(game.getPhaseHandler().getTurn());
if (latestState != null) { if (latestState != null) {
cardsAddedThisTurn.add(zt, latestState); cardsAddedThisTurn.add(zt, latestState);
}
} }
} }
}
if (zoneType != ZoneType.Battlefield) { if (zoneType != ZoneType.Battlefield) {
c.setTapped(false); c.setTapped(false);
} }
if (zoneType == (ZoneType.Graveyard) && c.isPermanent() && !c.isToken()) { if (zoneType == ZoneType.Graveyard && c.isPermanent() && !c.isToken()) {
c.getOwner().descend(); c.getOwner().descend();
}
} }
c.setZone(this); c.setZone(this);

View File

@@ -3,7 +3,7 @@ ManaCost:2 G
Types:Enchantment Types:Enchantment
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigMill | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, you may mill two cards. T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigMill | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, you may mill two cards.
SVar:TrigMill:DB$ Mill | NumCards$ 2 | Defined$ You | Optional$ True SVar:TrigMill:DB$ Mill | NumCards$ 2 | Defined$ You | Optional$ True
T:Mode$ ChangesZoneAll | ValidCards$ Land.YouOwn+nonToken | Origin$ Any | Destination$ Graveyard | TriggerZones$ Battlefield | ActivationLimit$ 1 | Execute$ TrigToken | TriggerDescription$ Whenever one or more land cards are put into your graveyard from anywhere for the first time each turn, create a 1/1 green Insect creature token. T:Mode$ ChangesZoneAll | ValidCards$ Land.YouOwn+nonToken | Origin$ Any | Destination$ Graveyard | TriggerZones$ Battlefield | FirstTime$ True | Execute$ TrigToken | TriggerDescription$ Whenever one or more land cards are put into your graveyard from anywhere for the first time each turn, create a 1/1 green Insect creature token.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_1_1_insect | TokenOwner$ You SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_1_1_insect | TokenOwner$ You
DeckHints:Ability$Delirium DeckHints:Ability$Delirium
DeckHas:Ability$Graveyard DeckHas:Ability$Graveyard

View File

@@ -1,7 +1,7 @@
Name:Graceful Takedown Name:Graceful Takedown
ManaCost:1 G ManaCost:1 G
Types:Sorcery Types:Sorcery
A:SP$ Pump | ValidTgts$ Creature.YouCtrl+enchanted | TargetMin$ 0 | TargetMax$ X | RememberObjects$ ThisTargetedCard | SubAbility$ DBPumpBis | SpellDescription$ Any number of target enchanted creatures you control and up to one other target creature you control each deal damage equal to their power to target creature you don't control. A:SP$ Pump | ValidTgts$ Creature.YouCtrl+enchanted | TargetMin$ 0 | TargetMax$ X | AILogic$ PowerDmg | RememberObjects$ ThisTargetedCard | SubAbility$ DBPumpBis | SpellDescription$ Any number of target enchanted creatures you control and up to one other target creature you control each deal damage equal to their power to target creature you don't control.
SVar:DBPumpBis:DB$ Pump | ValidTgts$ Creature.YouCtrl | TargetUnique$ True | TargetMin$ 0 | TargetMax 1 | RememberObjects$ ThisTargetedCard | SubAbility$ DBEachDamage SVar:DBPumpBis:DB$ Pump | ValidTgts$ Creature.YouCtrl | TargetUnique$ True | TargetMin$ 0 | TargetMax 1 | RememberObjects$ ThisTargetedCard | SubAbility$ DBEachDamage
SVar:X:Count$Valid Creature.YouCtrl+enchanted SVar:X:Count$Valid Creature.YouCtrl+enchanted
SVar:DBEachDamage:DB$ EachDamage | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Select target creature you don't control | DefinedDamagers$ Remembered | NumDmg$ Count$CardPower | StackDescription$ REP target creature_{c:ThisTargetedCard} | SubAbility$ DBCleanup SVar:DBEachDamage:DB$ EachDamage | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Select target creature you don't control | DefinedDamagers$ Remembered | NumDmg$ Count$CardPower | StackDescription$ REP target creature_{c:ThisTargetedCard} | SubAbility$ DBCleanup

View File

@@ -5,7 +5,7 @@ A:SP$ ChooseCard | ValidTgts$ Creature | TgtPrompt$ Choose two target creatures
SVar:DBUntap:DB$ Untap | Defined$ Targeted | SubAbility$ DBPutCounter | SpellDescription$ Untap them. SVar:DBUntap:DB$ Untap | Defined$ Targeted | SubAbility$ DBPutCounter | SpellDescription$ Untap them.
SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ P1P1 | CounterNum$ 2 | SubAbility$ DBPump | Put two +1/+1 counters on each of them. SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ P1P1 | CounterNum$ 2 | SubAbility$ DBPump | Put two +1/+1 counters on each of them.
SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Vigilance & Indestructible & Haste | SubAbility$ DBAddCombat | SpellDescription$ They gain vigilance, indestructible, and haste until end of turn. SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Vigilance & Indestructible & Haste | SubAbility$ DBAddCombat | SpellDescription$ They gain vigilance, indestructible, and haste until end of turn.
SVar:DBAddCombat:DB$ AddPhase | ExtraPhase$ Combat | ExtraPhaseDelayedTrigger$ DelTrigStatic | ExtraPhaseDelayedTriggerExcute$ TrigEffect | SpellDescription$ After this main phase, there is an additional combat phase. Only the chosen creatures can attack during that combat phase. SVar:DBAddCombat:DB$ AddPhase | ExtraPhase$ Combat | ExtraPhaseDelayedTrigger$ DelTrigStatic | ExtraPhaseDelayedTriggerExcute$ TrigEffect | ConditionPhases$ Main1,Main2 | SpellDescription$ After this main phase, there is an additional combat phase. Only the chosen creatures can attack during that combat phase.
SVar:DelTrigStatic:Mode$ Phase | Static$ True | Phase$ BeginCombat | TriggerDescription$ After this main phase, there is an additional combat phase. Only the chosen creatures can attack during that combat phase. SVar:DelTrigStatic:Mode$ Phase | Static$ True | Phase$ BeginCombat | TriggerDescription$ After this main phase, there is an additional combat phase. Only the chosen creatures can attack during that combat phase.
SVar:TrigEffect:DB$ Effect | StaticAbilities$ ForbidAttack | Duration$ UntilEndOfCombat SVar:TrigEffect:DB$ Effect | StaticAbilities$ ForbidAttack | Duration$ UntilEndOfCombat
SVar:ForbidAttack:Mode$ CantAttack | EffectZone$ Command | ValidCard$ Creature.YouCtrl+nonChosenCard | Description$ CARDNAME can't attack. SVar:ForbidAttack:Mode$ CantAttack | EffectZone$ Command | ValidCard$ Creature.YouCtrl+nonChosenCard | Description$ CARDNAME can't attack.

View File

@@ -4,7 +4,7 @@ Types:Artifact Creature Robot
PT:2/2 PT:2/2
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigEnergy | TriggerDescription$ When CARDNAME enters the battlefield, you get {E}{E} (two energy counters). T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigEnergy | TriggerDescription$ When CARDNAME enters the battlefield, you get {E}{E} (two energy counters).
SVar:TrigEnergy:DB$ PutCounter | Defined$ You | CounterType$ ENERGY | CounterNum$ 2 SVar:TrigEnergy:DB$ PutCounter | Defined$ You | CounterType$ ENERGY | CounterNum$ 2
T:Mode$ Attacks | ValidCard$ Creature.Artifact+YouCtrl | Execute$ TrigPutCounter | TriggerDescription$ Whenever an artifact creature you control attacks, you may pay {E}. If you do, put your choice of a +1/+1, first strike, or trample counter on that creature. T:Mode$ Attacks | ValidCard$ Creature.Artifact+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever an artifact creature you control attacks, you may pay {E}. If you do, put your choice of a +1/+1, first strike, or trample counter on that creature.
SVar:TrigPutCounter:AB$ PutCounter | Cost$ PayEnergy<1> | CounterType$ P1P1,First Strike,Trample | CounterNum$ 1 | Defined$ TriggeredAttackerLKICopy SVar:TrigPutCounter:AB$ PutCounter | Cost$ PayEnergy<1> | CounterType$ P1P1,First Strike,Trample | CounterNum$ 1 | Defined$ TriggeredAttackerLKICopy
DeckHas:Ability$Counters DeckHas:Ability$Counters
DeckHints:Type$Artifact DeckHints:Type$Artifact

View File

@@ -2,6 +2,6 @@ Name:Warlord's Elite
ManaCost:2 W ManaCost:2 W
Types:Creature Human Soldier Types:Creature Human Soldier
PT:4/4 PT:4/4
DeckHints:Type$Artifact
A:SP$ PermanentCreature | Cost$ 2 W tapXType<2/Artifact;Creature;Land/artifacts, creatures, and/or lands> | CostDesc$ As an additional cost to cast this spell, tap two untapped artifacts, creatures, and/or lands you control. | SpellDescription$ As an additional cost to cast this spell, tap two untapped artifacts, creatures, and/or lands you control. A:SP$ PermanentCreature | Cost$ 2 W tapXType<2/Artifact;Creature;Land/artifacts, creatures, and/or lands> | CostDesc$ As an additional cost to cast this spell, tap two untapped artifacts, creatures, and/or lands you control. | SpellDescription$ As an additional cost to cast this spell, tap two untapped artifacts, creatures, and/or lands you control.
DeckHints:Type$Artifact
Oracle:As an additional cost to cast this spell, tap two untapped artifacts, creatures, and/or lands you control. Oracle:As an additional cost to cast this spell, tap two untapped artifacts, creatures, and/or lands you control.