From a24af8d50e851db1cc6362200e7e36da0a583eb8 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Tue, 26 Dec 2023 04:55:53 +0100 Subject: [PATCH] Fix Stonebinder's Familiar not triggering from Nivmagus Elemental (#4424) * Fix Stonebinder's Familiar not triggering from Nivmagus Elemental * Fix so it only triggers for cards * Surveil triggers on empty library * Fix missing trigger * Fix trigger * fix script * Fix corner case freeze by trigger for loser (Kharn the Betrayer) * Fix loser making trigger decision e.g. Shakedown Heavy * Obeka fix when resolving after AP lost * fix script --------- Co-authored-by: tool4EvEr --- .../src/main/java/forge/game/GameAction.java | 10 ++-- .../java/forge/game/ability/AbilityUtils.java | 4 ++ .../game/ability/SpellAbilityEffect.java | 2 +- .../game/ability/effects/EncodeEffect.java | 4 +- .../game/ability/effects/EndTurnEffect.java | 6 ++- .../game/ability/effects/PermanentEffect.java | 8 +-- .../java/forge/game/card/CardFactoryUtil.java | 9 ++-- .../forge/game/cost/CostExileFromStack.java | 3 +- .../main/java/forge/game/player/Player.java | 52 +++++++++---------- .../forge/game/trigger/TriggerHandler.java | 4 ++ .../game/trigger/TriggerInvestigated.java | 5 +- .../forge/game/trigger/WrappedAbility.java | 14 +++-- .../res/cardsfolder/c/champions_of_tyr.txt | 2 +- .../res/cardsfolder/i/imposing_grandeur.txt | 2 +- forge-gui/res/cardsfolder/s/sirens_call.txt | 2 +- .../cardsfolder/s/stonebinders_familiar.txt | 2 +- .../res/cardsfolder/t/the_war_doctor.txt | 2 +- 17 files changed, 71 insertions(+), 60 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index e5640bf366d..ff614febefc 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -19,6 +19,7 @@ package forge.game; import com.google.common.base.Predicate; import com.google.common.collect.*; + import forge.GameCommand; import forge.StaticData; import forge.card.CardStateName; @@ -54,6 +55,7 @@ import forge.item.PaperCard; import forge.util.*; import forge.util.collect.FCollection; import forge.util.collect.FCollectionView; + import org.apache.commons.lang3.tuple.ImmutablePair; import java.util.*; @@ -902,14 +904,10 @@ public class GameAction { CardZoneTable table = new CardZoneTable(getLastState(AbilityKey.LastStateBattlefield, cause, params), getLastState(AbilityKey.LastStateGraveyard, cause, params)); CardCollection result = new CardCollection(); for (Card card : cards) { - if (cause != null) { - table.put(card.getZone().getZoneType(), ZoneType.Exile, card); - } + table.put(card.getZone().getZoneType(), ZoneType.Exile, card); result.add(exile(card, cause, params)); } - if (cause != null) { - table.triggerChangesZoneAll(game, cause); - } + table.triggerChangesZoneAll(game, cause); return result; } public final Card exile(final Card c, SpellAbility cause, Map params) { diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 060814f3d5c..bfd7950c36a 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -1537,6 +1537,10 @@ public class AbilityUtils { boolean alreadyPaid = false; for (Player payer : allPayers) { + if (!payer.isInGame()) { + // CR 800.4f + continue; + } if (unlessCost.equals("LifeTotalHalfUp")) { String halfup = Integer.toString(Math.max(0,(int) Math.ceil(payer.getLife() / 2.0))); cost = new Cost("PayLife<" + halfup + ">", true); diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index d99a34397e9..372748724ec 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -876,7 +876,7 @@ public abstract class SpellAbilityEffect { } } - public Player getNewChooser(final SpellAbility sa, final Player activator, final Player loser) { + public static Player getNewChooser(final SpellAbility sa, final Player activator, final Player loser) { // CR 800.4g final PlayerCollection options; if (loser.isOpponentOf(activator)) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java index 00596aa051c..4a92050245d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java @@ -7,10 +7,10 @@ import forge.game.GameLogEntryType; import forge.game.ability.AbilityKey; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; +import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.zone.ZoneType; import forge.util.CardTranslation; import forge.util.Localizer; @@ -57,7 +57,7 @@ public class EncodeEffect extends SpellAbilityEffect { moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); // move host card to exile - Card movedCard = game.getAction().moveTo(ZoneType.Exile, host, sa, moveParams); + Card movedCard = game.getAction().exile(new CardCollection(host), sa, moveParams).get(0); // choose a creature Card choice = player.getController().chooseSingleEntityForEffect(choices, sa, Localizer.getInstance().getMessage("lblChooseACreatureYouControlToEncode") + " ", false, null); diff --git a/forge-game/src/main/java/forge/game/ability/effects/EndTurnEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EndTurnEffect.java index 88f4aa036d8..8749c61d6a0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EndTurnEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EndTurnEffect.java @@ -17,7 +17,11 @@ public class EndTurnEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { final List enders = getDefinedPlayersOrTargeted(sa, "Defined"); - final Player ender = enders.isEmpty() ? sa.getActivatingPlayer() : enders.get(0); + Player ender = enders.isEmpty() ? sa.getActivatingPlayer() : enders.get(0); + if (!ender.isInGame()) { + ender = getNewChooser(sa, sa.getActivatingPlayer(), ender); + } + if (sa.hasParam("Optional") && !ender.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantEndTurn"), null)) { return; } diff --git a/forge-game/src/main/java/forge/game/ability/effects/PermanentEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PermanentEffect.java index 569acce591e..8976393bc51 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PermanentEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PermanentEffect.java @@ -9,7 +9,6 @@ import forge.game.Game; import forge.game.ability.AbilityKey; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; -import forge.game.card.CardCollectionView; import forge.game.card.CardZoneTable; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; @@ -30,12 +29,9 @@ public class PermanentEffect extends SpellAbilityEffect { CardZoneTable table = new CardZoneTable(); ZoneType previousZone = host.getZone().getZoneType(); - CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield(); - CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard(); - Map moveParams = AbilityKey.newMap(); - moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); - moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); + moveParams.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, game.copyLastStateGraveyard()); final Card c = game.getAction().moveToPlay(host, host.getController(), sa, moveParams); sa.setHostCard(c); diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index 5b6e7f55bcb..267021f1e2f 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -31,6 +31,7 @@ import forge.game.cost.CostPart; import forge.game.event.GameEventCardForetold; import forge.game.trigger.TriggerType; import forge.util.Localizer; + import org.apache.commons.lang3.StringUtils; import com.google.common.base.Predicate; @@ -78,6 +79,7 @@ import forge.game.trigger.TriggerHandler; import forge.game.zone.ZoneType; import forge.util.Lang; import forge.util.TextUtil; + import io.sentry.Breadcrumb; import io.sentry.Sentry; @@ -109,12 +111,9 @@ public class CardFactoryUtil { } final Game game = hostCard.getGame(); - CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield(); - CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard(); - Map moveParams = AbilityKey.newMap(); - moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); - moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); + moveParams.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, game.copyLastStateGraveyard()); hostCard.getGame().getAction().moveToPlay(hostCard, this, moveParams); } diff --git a/forge-game/src/main/java/forge/game/cost/CostExileFromStack.java b/forge-game/src/main/java/forge/game/cost/CostExileFromStack.java index ac1cfa0d102..545a5847362 100644 --- a/forge-game/src/main/java/forge/game/cost/CostExileFromStack.java +++ b/forge-game/src/main/java/forge/game/cost/CostExileFromStack.java @@ -19,6 +19,7 @@ package forge.game.cost; import forge.game.Game; import forge.game.card.Card; +import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; import forge.game.card.CardUtil; @@ -110,7 +111,7 @@ public class CostExileFromStack extends CostPart { if (si != null) { game.getStack().remove(si); } - game.getAction().exile(sa.getHostCard(), null, null); + game.getAction().exile(new CardCollection(sa.getHostCard()), null, null); } return true; } diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 86be271c33d..7abbf462562 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1122,38 +1122,36 @@ public class Player extends GameEntity implements Comparable { final CardCollection topN = getTopXCardsFromLibrary(num); - if (topN.isEmpty()) { - return; - } + if (!topN.isEmpty()) { + final ImmutablePair lists = getController().arrangeForSurveil(topN); + final CardCollection toTop = lists.getLeft(); + final CardCollection toGrave = lists.getRight(); - final ImmutablePair lists = getController().arrangeForSurveil(topN); - final CardCollection toTop = lists.getLeft(); - final CardCollection toGrave = lists.getRight(); + int numToGrave = 0; + int numToTop = 0; - int numToGrave = 0; - int numToTop = 0; - - if (toGrave != null) { - for (Card c : toGrave) { - ZoneType oZone = c.getZone().getZoneType(); - Card moved = getGame().getAction().moveToGraveyard(c, cause, params); - table.put(oZone, moved.getZone().getZoneType(), moved); - numToGrave++; + if (toGrave != null) { + for (Card c : toGrave) { + ZoneType oZone = c.getZone().getZoneType(); + Card moved = getGame().getAction().moveToGraveyard(c, cause, params); + table.put(oZone, moved.getZone().getZoneType(), moved); + numToGrave++; + } } - } - if (toTop != null) { - Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus. - for (Card c : toTop) { - getGame().getAction().moveToLibrary(c, cause, params); - numToTop++; + if (toTop != null) { + Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus. + for (Card c : toTop) { + getGame().getAction().moveToLibrary(c, cause, params); + numToTop++; + } + if (cause.hasParam("RememberKept")) { + cause.getHostCard().addRemembered(toTop); + } } - if (cause.hasParam("RememberKept")) { - cause.getHostCard().addRemembered(toTop); - } - } - getGame().fireEvent(new GameEventSurveil(this, numToTop, numToGrave)); + getGame().fireEvent(new GameEventSurveil(this, numToTop, numToGrave)); + } surveilThisTurn++; final Map runParams = AbilityKey.mapFromPlayer(this); @@ -2276,7 +2274,7 @@ public class Player extends GameEntity implements Comparable { public final void addInvestigatedThisTurn() { investigatedThisTurn++; final Map runParams = AbilityKey.mapFromPlayer(this); - runParams.put(AbilityKey.Num, investigatedThisTurn); + runParams.put(AbilityKey.FirstTime, investigatedThisTurn == 1); game.getTriggerHandler().runTrigger(TriggerType.Investigated, runParams, false); } public final void resetInvestigatedThisTurn() { diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java index 9143abcaa09..a03e6b67db3 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java @@ -531,6 +531,10 @@ public class TriggerHandler { host.addRemembered(triggeredCard); } + if (!sa.getActivatingPlayer().isInGame()) { + return; + } + sa.setStackDescription(sa.toString()); Player decider = null; diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerInvestigated.java b/forge-game/src/main/java/forge/game/trigger/TriggerInvestigated.java index e92940e1405..29700a92918 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerInvestigated.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerInvestigated.java @@ -71,11 +71,12 @@ public class TriggerInvestigated extends Trigger { return false; } - if (hasParam("OnlyFirst")) { - if ((int) runParams.get(AbilityKey.Num) != 1) { + if (hasParam("FirstTime")) { + if (!(boolean) runParams.get(AbilityKey.FirstTime)) { return false; } } + return true; } diff --git a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java index 3610f61775e..90306c34215 100644 --- a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java +++ b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java @@ -14,6 +14,7 @@ import forge.game.Game; import forge.game.GameEntityCounterTable; import forge.game.ability.AbilityKey; import forge.game.ability.ApiType; +import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardDamageMap; @@ -74,7 +75,7 @@ public class WrappedAbility extends Ability { ); private final SpellAbility sa; - private final Player decider; + private Player decider; boolean mandatory = false; @@ -465,7 +466,7 @@ public class WrappedAbility extends Ability { // ////////////////////////////////////// @Override public void resolve() { - final Game game = sa.getActivatingPlayer().getGame(); + final Game game = getActivatingPlayer().getGame(); final Trigger regtrig = getTrigger(); if (!(TriggerType.Always.equals(regtrig.getMode())) && !regtrig.hasParam("NoResolvingCheck")) { @@ -490,8 +491,13 @@ public class WrappedAbility extends Ability { } } - if (decider != null && !decider.getController().confirmTrigger(this)) { - return; + if (decider != null) { + if (!decider.isInGame()) { + decider = SpellAbilityEffect.getNewChooser(sa, getActivatingPlayer(), decider); + } + if (!decider.getController().confirmTrigger(this)) { + return; + } } if (!regtrig.hasParam("NoTimestampCheck")) { diff --git a/forge-gui/res/cardsfolder/c/champions_of_tyr.txt b/forge-gui/res/cardsfolder/c/champions_of_tyr.txt index 3b333cfd659..b97eae44765 100644 --- a/forge-gui/res/cardsfolder/c/champions_of_tyr.txt +++ b/forge-gui/res/cardsfolder/c/champions_of_tyr.txt @@ -8,7 +8,7 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.S SVar:DBBoon:DB$ Effect | Boon$ True | Duration$ Permanent | Triggers$ SpellCast SVar:SpellCast:Mode$ SpellCast | ValidCard$ Creature | ValidActivatingPlayer$ You | TriggerZones$ Command | Execute$ ReplEffAddCounter | TriggerDescription$ When you cast your next creature spell, that creature enters the battlefield with an additional +1/+1 counter, flying counter, and lifelink counter on it. SVar:ReplEffAddCounter:DB$ Effect | ReplacementEffects$ ETBAddCounter | RememberObjects$ TriggeredCard -SVar:ETBAddCounter:Event$ Moved | Origin$ Stack | Destination$ Battlefield | ValidCard$ Card.IsRemembered | ReplaceWith$ ETBAddExtraCounter +SVar:ETBAddCounter:Event$ Moved | Origin$ Stack | Destination$ Battlefield | ValidCard$ Card.IsRemembered | ReplacementResult$ Updated | ReplaceWith$ ETBAddExtraCounter SVar:ETBAddExtraCounter:DB$ PutCounter | ETB$ True | Defined$ ReplacedCard | CounterType$ P1P1,Flying,Lifelink | CounterNum$ 1 | SubAbility$ DBRemoveSelf SVar:DBRemoveSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile DeckHas:Ability$LifeGain|Counters & Keyword$Lifelink|Flying diff --git a/forge-gui/res/cardsfolder/i/imposing_grandeur.txt b/forge-gui/res/cardsfolder/i/imposing_grandeur.txt index 1b94ce4a170..2167ac60fb5 100644 --- a/forge-gui/res/cardsfolder/i/imposing_grandeur.txt +++ b/forge-gui/res/cardsfolder/i/imposing_grandeur.txt @@ -3,7 +3,7 @@ ManaCost:4 R Types:Sorcery A:SP$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBDraw | SpellDescription$ Each player may discard their hand and draw cards equal to the greatest mana value of a commander they own on the battlefield or in the command zone. SVar:DBDraw:DB$ Draw | UnlessCost$ Discard<1/Hand> | UnlessPayer$ Remembered | UnlessSwitched$ True | Defined$ Remembered | NumCards$ X -SVar:X:Count$ValidBattlefield,Command Card.IsCommander+YouOwn$GreatestCMC +SVar:X:Count$ValidBattlefield,Command Card.IsCommander+RememberedPlayerOwn$GreatestCMC DeckHas:Ability$Discard AI:RemoveDeck:NonCommander Oracle:Each player may discard their hand and draw cards equal to the greatest mana value of a commander they own on the battlefield or in the command zone. diff --git a/forge-gui/res/cardsfolder/s/sirens_call.txt b/forge-gui/res/cardsfolder/s/sirens_call.txt index 1bcd15b7752..8bac361fa49 100644 --- a/forge-gui/res/cardsfolder/s/sirens_call.txt +++ b/forge-gui/res/cardsfolder/s/sirens_call.txt @@ -1,7 +1,7 @@ Name:Siren's Call ManaCost:U Types:Instant -A:SP$ Effect | StaticAbilities$ MustAttack | ActivationPhases$ Upkeep->BeginCombat | OpponentTurn$ True | SpellDescription$ Cast this spell only during an opponent's turn, before attackers are declared. Creatures the active player controls attack this turn if able. At the beginning of the next end step, destroy all non-Wall creatures that player controls that didn't attack this turn. Ignore this effect for each creature the player didn't control continuously since the beginning of the turn. | SubAbility$ DestroyPacifist +A:SP$ Effect | StaticAbilities$ MustAttack | ActivationPhases$ Upkeep->BeginCombat | ActivationFirstCombat$ True | OpponentTurn$ True | SpellDescription$ Cast this spell only during an opponent's turn, before attackers are declared. Creatures the active player controls attack this turn if able. At the beginning of the next end step, destroy all non-Wall creatures that player controls that didn't attack this turn. Ignore this effect for each creature the player didn't control continuously since the beginning of the turn. | SubAbility$ DestroyPacifist SVar:MustAttack:Mode$ MustAttack | EffectZone$ Command | AffectedZone$ Battlefield | ValidCreature$ Creature.ActivePlayerCtrl | Description$ Creatures the active player controls attack this turn if able. SVar:DestroyPacifist:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigDestroy | TriggerDescription$ At the beginning of the next end step, destroy all non-Wall creatures that player controls that didn't attack this turn. SVar:TrigDestroy:DB$ DestroyAll | ValidCards$ Creature.ActivePlayerCtrl+notAttackedThisTurn+nonWall+notFirstTurnControlled | SubAbility$ DBCleanup diff --git a/forge-gui/res/cardsfolder/s/stonebinders_familiar.txt b/forge-gui/res/cardsfolder/s/stonebinders_familiar.txt index ca0532e5e2c..771c18353c9 100644 --- a/forge-gui/res/cardsfolder/s/stonebinders_familiar.txt +++ b/forge-gui/res/cardsfolder/s/stonebinders_familiar.txt @@ -2,7 +2,7 @@ Name:Stonebinder's Familiar ManaCost:W Types:Creature Spirit Dog PT:1/1 -T:Mode$ ChangesZoneAll | Destination$ Exile | TriggerZones$ Battlefield | Execute$ TrigPutcounter | PlayerTurn$ True | ActivationLimit$ 1 | TriggerDescription$ Whenever one or more cards are put into exile during your turn, put a +1/+1 counter on CARDNAME. This ability triggers only once each turn. +T:Mode$ ChangesZoneAll | ValidCard$ Card.nonToken+nonCopiedSpell | Destination$ Exile | TriggerZones$ Battlefield | Execute$ TrigPutcounter | PlayerTurn$ True | ActivationLimit$ 1 | TriggerDescription$ Whenever one or more cards are put into exile during your turn, put a +1/+1 counter on CARDNAME. This ability triggers only once each turn. SVar:TrigPutcounter:DB$ PutCounter | CounterType$ P1P1 | Defined$ Self | CounterNum$ 1 DeckHas:Ability$Counters Oracle:Whenever one or more cards are put into exile during your turn, put a +1/+1 counter on Stonebinder's Familiar. This ability triggers only once each turn. diff --git a/forge-gui/res/cardsfolder/t/the_war_doctor.txt b/forge-gui/res/cardsfolder/t/the_war_doctor.txt index 6f0926fc679..238146a4cdf 100644 --- a/forge-gui/res/cardsfolder/t/the_war_doctor.txt +++ b/forge-gui/res/cardsfolder/t/the_war_doctor.txt @@ -3,7 +3,7 @@ ManaCost:2 R W Types:Legendary Creature Time Lord Doctor PT:3/5 T:Mode$ PhaseOutAll | ValidCards$ Permanent.phasedOutOther | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever one or more other permanents phase out and whenever one or more other cards are put into exile from anywhere, put a time counter on CARDNAME. -T:Mode$ ChangesZoneAll | ValidCards$ Card.Other | Origin$ Any | Destination$ Exile | TriggerZones$ Battlefield | Execute$ TrigPutCounter | Secondary$ True | TriggerDescription$ Whenever one or more other permanents phase out and whenever one or more other cards are put into exile from anywhere, put a time counter on CARDNAME. +T:Mode$ ChangesZoneAll | ValidCards$ Card.Other+nonToken+nonCopiedSpell | Origin$ Any | Destination$ Exile | TriggerZones$ Battlefield | Execute$ TrigPutCounter | Secondary$ True | TriggerDescription$ Whenever one or more other permanents phase out and whenever one or more other cards are put into exile from anywhere, put a time counter on CARDNAME. SVar:TrigPutCounter:DB$ PutCounter | CounterType$ TIME T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDamage | TriggerDescription$ Whenever CARDNAME attacks, it deals damage equal to the number of time counters on it to any target. If a creature dealt damage this way would die this turn, exile it instead. SVar:TrigDamage:DB$ DealDamage | ValidTgts$ Any | DamageSource$ TriggeredAttackerLKICopy | NumDmg$ Count$CardCounters.TIME | ReplaceDyingDefined$ Targeted.Creature